Використання Django auth UserAdmin для користувацької моделі користувача


82

З документів Django.Contrib.Auth :

Розширення користувача за замовчуванням Django Якщо вас цілком влаштовує модель користувача Django, і ви просто хочете додати додаткову інформацію про профіль, ви можете просто зробити підклас django.contrib.auth.models.AbstractUserта додати власні поля профілю. Цей клас забезпечує повну реалізацію користувача за замовчуванням як абстрактну модель.

Сказано і зроблено. Я створив нову модель, як показано нижче:

class MyUser(AbstractUser):
  some_extra_data = models.CharField(max_length=100, blank=True)

Це відображається в адміністраторі майже як стандарт Django User. Однак найважливіша відмінність адміністратора полягає в тому, що поля для (пере) встановлення пароля немає, а замість цього відображається звичайний CharField. Чи справді мені доводиться перевизначати матеріали в адмін-конфігурі, щоб це працювало? Якщо так, то як я можу зробити це дещо СУХО (тобто без копіювання матеріалів із джерела Django ... eww ...)?

Відповіді:


135

Покопавшись якийсь час навколо вихідного коду Django, я знайшов дієвий дух. Я не повністю задоволений цим рішенням, але, схоже, це працює. Не соромтеся пропонувати кращі рішення!


Django використовує, UserAdminщоб зробити приємний зовнішній вигляд Userмоделі. Просто використовуючи це у нашому admin.py-файлі, ми можемо отримати такий же вигляд для нашої моделі.

from django.contrib.auth.admin import UserAdmin
admin.site.register(MyUser, UserAdmin)

Однак одне лише це, мабуть, не є гарним рішенням, оскільки адміністратор Django не відображатиме жодного з ваших спеціальних полів. Цьому є дві причини:

  • UserAdminвикористовує UserChangeFormяк форму, яка буде використана при зміні об’єкта, який, у свою чергу, використовує Userяк свою модель.
  • UserAdminвизначає formsets-property, який пізніше використовується UserChangeForm, який не включає ваші спеціальні поля.

Отже, я створив спеціальну форму зміни, яка перевантажує внутрішній клас Meta, щоб форма зміни використовувала правильну модель. Мені також довелося перевантажити, UserAdminщоб додати свої спеціальні поля до набору полів, що є частиною цього рішення, яке мені трохи не подобається, оскільки це виглядає трохи потворно. Не соромтеся пропонувати вдосконалення!

from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.forms import UserChangeForm

class MyUserChangeForm(UserChangeForm):
    class Meta(UserChangeForm.Meta):
        model = MyUser

class MyUserAdmin(UserAdmin):
    form = MyUserChangeForm

    fieldsets = UserAdmin.fieldsets + (
            (None, {'fields': ('some_extra_data',)}),
    )


admin.site.register(MyUser, MyUserAdmin)

Чудово! Це саме те, що я шукав, я роблю те саме зі своєю користувацькою моделлю користувача, використовуючи AbstractUser. Це рішення чудово працює, деякі налаштування (приховування полів, додавання ваших "some_extra_data" у "Особиста інформація") теж було б непогано, але наразі ви зробили мій день. Спасибі @nico
Dachmt

Перевантаження наборів полів de UserAdmin потворно? Це працює. Дякую.
allcaps

6
Також зверніть увагу, що вам потрібно замінити форму створення користувача відповідно до відповіді @ kdh454.
Thane Brimhall

Це ідеально, це зовсім не брудно. Документація django вказує на цю статтю: docs.djangoproject.com/en/2.0/topics/auth/customizing/… ... але мені не вдалося змусити це працювати, вона скаржилася на деякі обмеження зовнішнього ключа користувача. Може бути пов’язане лише з моїм проектом, тим не менш, ваше рішення працює!
Володимир Мартон

57

Відповідь nico була надзвичайно корисною, але я виявив, що Django все ще посилається на модель користувача при створенні нового користувача.

Квиток № 19353 стосується цієї проблеми.

Для того, щоб це виправити, мені довелося зробити ще кілька доповнень admin.py

admin.py:

from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.forms import UserChangeForm, UserCreationForm
from main.models import MyUser
from django import forms


class MyUserChangeForm(UserChangeForm):
    class Meta(UserChangeForm.Meta):
        model = MyUser


class MyUserCreationForm(UserCreationForm):
    class Meta(UserCreationForm.Meta):
        model = MyUser

    def clean_username(self):
        username = self.cleaned_data['username']
        try:
            MyUser.objects.get(username=username)
        except MyUser.DoesNotExist:
            return username
        raise forms.ValidationError(self.error_messages['duplicate_username'])


class MyUserAdmin(UserAdmin):
    form = MyUserChangeForm
    add_form = MyUserCreationForm
    fieldsets = UserAdmin.fieldsets + (
        (None, {'fields': ('extra_field1', 'extra_field2',)}),
    )

admin.site.register(MyUser, MyUserAdmin)

Ваше рішення спрацювало для мене бездоганно. Дуже дякую!
guinunez

3
Зверніть увагу , що починаючи з 1.6, виклик forms.ValidationErrorмає другий аргумент: code='duplicate_username': github.com/django/django/blob/1.6/django/contrib/auth / ... Крім того , я спробував це на 1.6.5 і, по якій - то причини, я не потрібно було UserChangeFormвзагалі перевизначати, і форма змін добре працювала з моїм користувацьким полем. Я не можу пояснити, чому. Здається, це не мало спрацювати через присутність UserChangeFormкласу model = User: github.com/django/django/blob/1.6.5/django/contrib/auth/…
Нік

2
Квиток № 19353 FYI тепер виправлено, що по суті означає, що ваше рішення може бути зайвим. Якщо ми просто використовуємо рішення @ nip3o, ми повинні бути хорошими.
kstratis

50

Більш просте рішення, admin.py:

from django.contrib.auth.admin import UserAdmin
from main.models import MyUser

class MyUserAdmin(UserAdmin):
    model = MyUser

    fieldsets = UserAdmin.fieldsets + (
            (None, {'fields': ('some_extra_data',)}),
    )

admin.site.register(MyUser, MyUserAdmin)

Django буде правильно посилатися на модель MyUser для створення та модифікації. Я використовую Django 1.6.2.


Я не знаю, чому, але я намагався отримувати "TypeError: непідтримувані типи операндів для +: 'NoneType' і 'кортеж'", намагаючись це.
Чейз Робертс

Я пропоную вам перевірити UserAdmin.fieldsets і перевірити, чи немає його
Rômulo Collopy

1
це найбільш оновлена ​​відповідь, і вона повинна бути прийнятою
блиск

1
Натомість дозвольте None, ви можете ввести рядок, і він буде використовуватися як заголовок розділу в адміністративній формі
Гійом Лебретон,

Погодьтеся з @brillout, виявив, що це найкраща відповідь. До того ж, ви можете отримати цю помилку, яка має сенс (Python 3.8, Django 3):ERRORS: <class 'users.admin.CustomerUserAdmin'>: (admin.E005) Both 'fieldsets' and 'fields' are specified.
nicorellius

9

Відповідь cesc не спрацювала для мене, коли я спробував додати власне поле до форми створення. Можливо, це змінилося з 1.6.2? У будь-якому випадку, я виявив, що додавання поля до обох наборів полів і add_fieldsets зробили свою справу.

ADDITIONAL_USER_FIELDS = (
    (None, {'fields': ('some_additional_field',)}),
)

class MyUserAdmin(UserAdmin):
    model = MyUser

    add_fieldsets = UserAdmin.add_fieldsets + ADDITIONAL_USER_FIELDS
    fieldsets = UserAdmin.fieldsets + ADDITIONAL_USER_FIELDS

admin.site.register(MyUser, MyUserAdmin)

2

Ще одне подібне рішення ( взяв звідси ):

from __future__ import unicode_literals

from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import AbstractUser
from django.utils.translation import ugettext_lazy as _

from .models import User


class UserAdminWithExtraFields(UserAdmin):

    def __init__(self, *args, **kwargs):
        super(UserAdminWithExtraFields, self).__init__(*args, **kwargs)

        abstract_fields = [field.name for field in AbstractUser._meta.fields]
        user_fields = [field.name for field in self.model._meta.fields]

        self.fieldsets += (
            (_('Extra fields'), {
                'fields': [
                    f for f in user_fields if (
                        f not in abstract_fields and
                        f != self.model._meta.pk.name
                    )
                ],
            }),
        )


admin.site.register(User, UserAdminWithExtraFields)

-1

Просто зробіть наступне

from django.contrib import admin
from .models import MyUser

admin.site.register(MyUser, admin.ModelAdmin)

Це воно. Будуть показані всі поля моделі MyUser. Список полів UserAdmin для показу має жорсткий код (див. Його вихідний код), але ModelAdmin - ні. Це гнучко.


Не рекомендовано. Тут буде використано типовий ModelAdmin, а не налаштований UserAdmin. Результат - безлад.
dqd
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.