使用Django auth UserAdmin创建自定义用户模型


79

来自Django.Contrib.Auth文档

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

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

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

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

Answers:


132

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


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

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)

大!这正是我想要的,我正在使用AbstractUser对自定义用户模型进行相同的操作。此解决方案效果很好,有些自定义设置(隐藏字段,在“个人信息”中添加“ some_extra_data”)也不错,但是现在您已经过得很愉快。感谢@nico
Dachmt 2013年

de UserAdmin字段集的重载难看吗?有用。谢谢。
allcaps 2013年

6
还要注意,根据@ kdh454的答案,您需要覆盖用户创建表单。
塔娜·布里姆霍尔

这很完美,一点也不乱。django文档指向本文:docs.djangoproject.com/en/2.0/topics/auth/customizing / ... ...但是我无法使其工作,它抱怨某些用户外键约束。可能仅与我的项目有关,但是您的解决方案仍然有效!
弗拉基米尔·马顿

56

nico的回答非常有帮助,但是我发现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)

您的解决方案对我而言完美无缺。非常感谢!
guinunez 2014年

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/…–
Nick

2
FYI票证#19353现在已修复,这实际上意味着您的解决方案可能是多余的。如果我们只使用@ nip3o的解决方案,那应该很好。
kstratis '16

49

一个更简单的解决方案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'和'tuple'”的信息。
Chase Roberts

我建议你检查UserAdmin.fieldsets,看看它的无
罗慕洛Collopy

1
这是最新的答案,应该是被接受的答案
brillout 2016年

1
取而代之的是None,您可以输入一个字符串,该字符串将用作管理表单中该部分的标题
Guillaume Lebreton 18-10-4

同意@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起已更改?无论哪种方式,我都发现将字段添加到两个fieldset和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)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.