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 个用户,这有点耗时。而且这无论如何都不是用户友好的(管理员不适合开发人员)。
所以为了找到更好的方法,我绞尽脑汁好几天了:
- 一个友好的 add/change 特殊表单,我可以在其中编辑一些用户字段
- 一个友好的 add/change Professional 表单,我可以在其中编辑一些用户字段
- 一个友好的 add/change 品牌表单,我可以在其中编辑一些用户字段
- 一个友好的add/change工作人员表单,我可以在其中编辑一些用户字段
- 保留原来的用户更改表,高级版
是否可以使用 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)
并重定向 ParticularAdmin
、ProfessionalAdmin
、BrandAdmin
和 StaffAdmin
的 add_view()
和 change_view()
。
当我开始为我的应用程序建模时,因为我有多种类型的用户,所以我首先考虑完全定义我自己的用户模型,或者对其进行子类化。但是我最终选择遵循标准推荐的方式,使用 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 个用户,这有点耗时。而且这无论如何都不是用户友好的(管理员不适合开发人员)。
所以为了找到更好的方法,我绞尽脑汁好几天了:
- 一个友好的 add/change 特殊表单,我可以在其中编辑一些用户字段
- 一个友好的 add/change Professional 表单,我可以在其中编辑一些用户字段
- 一个友好的 add/change 品牌表单,我可以在其中编辑一些用户字段
- 一个友好的add/change工作人员表单,我可以在其中编辑一些用户字段
- 保留原来的用户更改表,高级版
是否可以使用 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)
并重定向 ParticularAdmin
、ProfessionalAdmin
、BrandAdmin
和 StaffAdmin
的 add_view()
和 change_view()
。