Django - 许多类型的用户 - add/change 实例包括用户字段

Django - many types of User - add/change instances including User fields

当我开始为我的应用程序建模时,因为我有多种类型的用户,所以我首先考虑完全定义我自己的用户模型,或者对其进行子类化。但是我最终选择遵循标准推荐的方式,使用 OneToOneField :

class Member(models.Model):
    class Meta:
        abstract = True

    user = models.OneToOneField(User)

    ... more fields ..

class Particular(Member):
    ... even more fields ...

class Professional(Member):
    ... even more fields ...

class Brand(Member):
    ... even more fields ...

class Staff(Member):
    ... even more fields ...

但是,现在我遇到了与管理员打交道的问题。我有我的 4 XxxAdmin,但我无法编辑用户名、first_name、last_name 和电子邮件字段,因为它们位于用户模型中,没有外键指向我的自定义成员类型。我无法添加成员,因为它需要先创建一个用户,并且我禁用了外键 select 字段,因为我们有超过 100.000 个用户,这有点耗时。而且这无论如何都不是用户友好的(管理员不适合开发人员)。

所以为了找到更好的方法,我绞尽脑汁好几天了:

是否可以使用 ParticularAdmin、ProfessionalAdmin、BrandAdmin 和 StaffAdmin 来完成它,或者我应该在 UserAdmin 中定义自定义视图?

详细信息:Django 1.8,当前未安装 extension/plugin。

我 运行 在 Django 中遇到过几次这个问题。我仍然不确定是否存在完美的解决方案。但是我建议使用 pre_save 信号为每个成员类型创建 django 用户。

class Particular(models.Model):
    user=models.OneToOneField(User,null=True,blank=True,related_name='particular')
    first_name=models.CharField(.....)
    last_name=models.CharField(.....)
    username=models.CharField(.....)
    email=models.EmailField(.....)
    password=models.CharField(....)


from django.db.models.signals import pre_save,post_save
from django.contrib.auth.hashers import make_password

def particular_pre_save(sender,**kwargs):
   instance=kwargs['instance']
   if instance.pk is None:#new instance
       U=User()
   else:
       U=instance.user


   U.first_name=instance.first_name
   U.last_name=instance.last_name
   U.username=instance.username
   U.password=make_password(instance.password)#hash password

   U=U.save()
   # do something with user U, perhaps add permissions or groups
   instance.user=U
   instance.password=make_password(instance.password)#hash password

pre_save.connect(particular_pre_save,Particular)
#####in forms.py
class ParticularForm(forms.Form):
    password=forms.CharField(widget=forms.PasswordInput)
    class Meta:
         model = Particular
         exclude = ['user']

每次有人 creates/changes 特定实例时,用户也是 created/changed。然后,您必须在 Django 管理 class 中使用 ParticularForm。这种方法会产生一些冗余,因为用户 table 和特定 table 都包含相同的数据。

您也可以为用户模型添加类似的pre_save,以便在用户模型更改时更新特定模型。代码未经测试。

def user_post_save(sender,**kwargs):
   instance=kwargs['instance']
   query=instance.particular.all()
   if query:
      P=query[0]
      P.first_name=instance.first_name
      P.last_name=instance.last_name
      P.username=instance.username
      P.password=make_password(instance.password)#hash password

      P.save()

post_save.connect(user_post_save,User)    

目前,这是我找到的最佳解决方案:

admin.site.unregister(User)
admin.register(User)
class UserAdmin(auth.admin.UserAdmin):

    def get_urls(self):
        urls = super(UserAdmin, self).get_urls()
        my_urls = [
            url(r'^particular/add/$', self.add_part_view),
            url(r'^professional/add/$', self.add_pro_view),
            url(r'^brand/add/$', self.add_brand_view),
            url(r'^staff/add/$', self.add_staff_view),
            url(r'^particular/([0-9]+)/$', self.change_part_view),
            url(r'^professional/([0-9]+)/$', self.change_pro_view),
            url(r'^brand/([0-9]+)/$', self.change_brand_view),
            url(r'^staff/([0-9]+)/$', self.change_staff_view),
        ]
        return my_urls + urls

    def add_part_view(self, request, form_url='', extra_context=None):
        self.inlines = [ParticularInline]
        return self.add_view(request, form_url, extra_context)

    def change_part_view(self, request, object_id, form_url='', extra_context=None):
        self.inlines = [ParticularInline]
        return self.change_view(request, object_id, form_url, extra_context)

    def add_pro_view(self, request, form_url='', extra_context=None):
        self.inlines = [ProfessionalInline]
        return self.add_view(request, form_url, extra_context)

    def change_pro_view(self, request, object_id, form_url='', extra_context=None):
        self.inlines = [ProfessionalInline]
        return self.change_view(request, object_id, form_url, extra_context)

    def add_brand_view(self, request, form_url='', extra_context=None):
        self.inlines = [BrandInline]
        return self.add_view(request, form_url, extra_context)

    def change_brand_view(self, request, object_id, form_url='', extra_context=None):
        self.inlines = [BrandInline]
        return self.change_view(request, object_id, form_url, extra_context)

    def add_staff_view(self, request, form_url='', extra_context=None):
        self.inlines = [StaffInline]
        return self.add_view(request, form_url, extra_context)

    def change_staff_view(self, request, object_id, form_url='', extra_context=None):
        self.inlines = [StaffInline]
        return self.change_view(request, object_id, form_url, extra_context)

并重定向 ParticularAdminProfessionalAdminBrandAdminStaffAdminadd_view()change_view()