Использование 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. Мне действительно нужно переопределить что-то в admin-config, чтобы это работало? Если да, то как я могу сделать это СУХИМ способом (то есть без копирования материала из исходного кода Django ... фу ...)?

nip3o
источник

Ответы:

135

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


Django использует UserAdminдля визуализации привлекательной админской Userмодели. Просто используя это в нашем admin.pyфайле, мы можем получить такой же вид для нашей модели.

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

Однако это само по себе, вероятно, не является хорошим решением, поскольку Django Admin не будет отображать какие-либо из ваших специальных полей. На это есть две причины:

  • UserAdminиспользует UserChangeFormкак форму, которая будет использоваться при изменении объекта, который, в свою очередь, использует Userкак свою модель.
  • UserAdminопределяет formsets-свойство, которое позже будет использоваться 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)
nip3o
источник
Большой! Это именно то, что я искал, я делаю то же самое со своей пользовательской моделью, используя AbstractUser. Это решение отлично работает, некоторая настройка (скрытие полей, добавление ваших 'some_extra_data' в 'Personal info') тоже было бы неплохо, но сейчас вы сделали мой день. Спасибо @nico
Dachmt
Уродливая перегрузка наборов полей де UserAdmin? Оно работает. Благодарю.
allcaps 09
6
Также обратите внимание, что вам нужно переопределить форму создания пользователя согласно ответу @ kdh454.
Thane Brimhall
Это идеально, это совсем не беспорядок. Документация django указывает на эту статью: docs.djangoproject.com/en/2.0/topics/auth/customizing/… ... но я не смог заставить его работать с этим, он жаловался на некоторые ограничения внешнего ключа пользователя. Может быть связано только с моим проектом, но ваше решение работает!
Владимир Мартон
57

Ответ Нико был чрезвычайно полезным, но я обнаружил, что Django все еще ссылается на модель User при создании нового пользователя.

Билет № 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)
kdh454
источник
Ваше решение сработало для меня безупречно. Большое спасибо!
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 теперь исправлен, что по сути означает, что ваше решение может быть избыточным. Если мы просто воспользуемся решением @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.

cesc
источник
Я не знаю почему, но я продолжал получать "TypeError: неподдерживаемые типы операндов для +: 'NoneType' и 'tuple'", пытаясь это сделать.
Чейз Робертс
Я предлагаю вам проверить UserAdmin.fieldsets и посмотреть, нет ли его
Rômulo Collopy
1
это самый обновленный ответ, и он должен быть принятым
brillout
1
Вместо этого let 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)
aidnani8
источник
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