将 Django auth UserAdmin 用于自定义用户模型

Posted

技术标签:

【中文标题】将 Django auth UserAdmin 用于自定义用户模型【英文标题】:Using Django auth UserAdmin for a custom user model 【发布时间】:2013-02-07 08:46:46 【问题描述】:

来自Django.Contrib.Auth docs:

扩展 Django 的默认用户 如果您对 Django 的 User 模型非常满意,并且只想添加一些额外的配置文件信息,您可以简单地继承 django.contrib.auth.models.AbstractUser 并添加您的自定义配置文件字段。此类提供默认 User 作为抽象模型的完整实现。

说完了。我创建了一个如下所示的新模型:

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

这几乎像 Django 的标准 User 一样显示在 admin 中。但是,在 admin 中最重要的区别是密码(重新)设置字段不存在,而是显示一个普通的 CharField。我真的必须覆盖 admin-config 中的内容才能使其正常工作吗?如果是这样,我怎么能以某种干燥的方式做到这一点(即不从 Django 源代码复制东西...... eww......)?

【问题讨论】:

【参考方案1】:

在研究了 Django 源代码一段时间后,我发现了一个可行的灵魂。我对这个解决方案并不完全满意,但它似乎有效。欢迎提出更好的解决方案!


Django 使用UserAdminUser 模型呈现漂亮的管理员外观。通过在我们的admin.py-文件中使用它,我们可以获得相同的模型外观。

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

但是,仅此一项可能不是一个好的解决方案,因为 Django Admin 不会显示您的任何特殊字段。这有两个原因:

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 de UserAdmin 字段集重载难看?有用。谢谢。 另请注意,根据@kdh454 的回答,您需要覆盖用户创建表单。 这很完美,一点也不乱。 django 文档指向这篇文章:docs.djangoproject.com/en/2.0/topics/auth/customizing/… ...但我无法让它与它一起工作,它抱怨一些用户外键约束。可能仅与我的项目有关,但您的解决方案有效!【参考方案2】:

一个更简单的解决方案,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: unsupported operand type(s) for +: 'NoneType' and 'tuple'”。 我建议你检查一下 UserAdmin.fieldsets 看看它是否为 None 取而代之的是None,你可以输入一个字符串,它将作为管理表单中的部分标题 同意@brillout,发现这是最好的答案。要添加,您可能会收到此错误,这是有道理的(Python 3.8,Django 3):ERRORS: <class 'users.admin.CustomerUserAdmin'>: (admin.E005) Both 'fieldsets' and 'fields' are specified.【参考方案3】:

nico 的回答非常有帮助,但我发现 Django 在创建新用户时仍然引用 User 模型。

Ticket #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)

【讨论】:

请注意,从 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/… FYI ticket #19353 现已修复,这基本上意味着您的解决方案可能是多余的。如果我们只使用@nip3o 的解决方案,我们应该会很好。【参考方案4】:

当我尝试将自定义字段添加到创建表单时,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)

【讨论】:

【参考方案5】:

另一个类似的解决方案(Took from here):

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)

【讨论】:

以上是关于将 Django auth UserAdmin 用于自定义用户模型的主要内容,如果未能解决你的问题,请参考以下文章

Django admin 中group的视图怎么添加选择user

如何在Django Admin中的用户变更页面添加描述?

Django 默认 user 表的替换和扩充

Django UserAdmin的add_fieldsets?

Django UserAdmin 自定义

python Django Admin:隐藏UserAdmin的权限,为GroupAdmin排除无用的权限