如何在 Django ModelAdmin 中创建和保存动态字段?
How do I create and save dynamic fields in Django ModelAdmin?
我有一些型号:
class GroupType(models.Model):
name = models.CharField(max_length=255)
class Group(models.Model):
name = models.CharField(max_length=255)
group_type = models.ForeignKey(GroupType)
class Person(models.Model):
name = models.CharField(max_length=255)
groups = models.ManyToManyField(Group, related_name="+")
但在 ModelAdmin 中,我想为每个组类型动态添加字段。因此,如果我有两个组类型 public
、private
,我希望管理表单显示两个字段 public_groups
和 private_groups
而不是实际的数据库字段 groups
.
更多信息
我已经尝试创建自定义表单来动态添加字段:
class PersonAdminForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(PersonAdminForm, self).__init__(*args, **kwargs)
new_fields = {}
for group_type in GroupType.objects.all():
field_name = "{0}_groups".format(group_type.name.lower())
qs = Group.objects.filter(group_type=group_type)
field_field = forms.ModelMultipleChoiceField(queryset=qs)
new_fields[field_name] = field_field
self.fields.update(new_fields)
class Meta:
model = Person
fields = '__all__'
就将字段添加到表单而言,这似乎可以解决问题。但是保存这些字段并将它们添加到 PersonAdmin
是行不通的。如果我将字段显式添加到 PersonAdmin
上的 fields
属性,我会得到:
FieldError: Unknown field(s) (public_groups, private_groups) specified for Person. Check fields/fieldsets/exclude attributes of class PersonAdmin.
我在尝试通过自定义 get_formsets
方法添加它们 "dynamically" 时也得到了同样的结果:
def get_fieldsets(self, request, obj=None):
fieldsets = super(PersonAdmin, self).get_fieldsets(request, obj)
print(self.form.fields)
fieldsets[0][1]['fields'].extend(['public_groups', 'private_groups'])
return fieldsets
以下代码运行良好。 ModelForm
上重写的 __init__
和 clean
方法添加动态字段并定义值的保存方式。
覆盖的 get_form
和 get_fieldsets
与 fieldsets
属性一起 AdminModel
确保动态表单字段显示在管理中。
class PersonAdminForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(PersonAdminForm, self).__init__(*args, **kwargs)
new_fields = {}
initial = self.instance.groups.all()
for group_type in GroupType.objects.all():
field_name = '{0}_groups'.format(group_type.name.lower())
qs = Group.objects.filter(group_type=group_type)
field = forms.ModelMultipleChoiceField(
queryset=qs,
required=False,
initial=initial,
)
new_fields[field_name] = field
self.fields.update(new_fields)
def clean(self):
cleaned_data = super(PersonAdminForm, self).clean()
groups = []
for group_type in GroupType.objects.all():
gt_name = '{0}_groups'.format(group_type.name.lower())
groups.extend(cleaned_data.get(gt_name))
self.instance.groups.clear()
self.instance.groups.add(*groups)
return cleaned_data
class Meta:
model = Person
fields = '__all__'
@admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
form = PersonAdminForm
# using the fieldsets attribute instead of fields in order to dynamically
# add group type fields later.
fieldsets = (
(None, {
'fields': (
'name',
),
}),
)
def get_form(self, request, obj=None, **kwargs):
kwargs['fields'] = flatten_fieldsets(self.declared_fieldsets)
return super(PersonAdmin, self).get_form(request, obj, **kwargs)
def get_fieldsets(self, request, obj=None):
fieldsets = super(PersonAdmin, self).get_fieldsets(request, obj)
newfieldsets = list(fieldsets)
fields = []
for group_type in GroupType.objects.all():
fields.append('{0}_groups'.format(group_type.name.lower()))
newfieldsets.append(['Groups', {'fields': fields}])
return newfieldsets
我有一些型号:
class GroupType(models.Model):
name = models.CharField(max_length=255)
class Group(models.Model):
name = models.CharField(max_length=255)
group_type = models.ForeignKey(GroupType)
class Person(models.Model):
name = models.CharField(max_length=255)
groups = models.ManyToManyField(Group, related_name="+")
但在 ModelAdmin 中,我想为每个组类型动态添加字段。因此,如果我有两个组类型 public
、private
,我希望管理表单显示两个字段 public_groups
和 private_groups
而不是实际的数据库字段 groups
.
更多信息
我已经尝试创建自定义表单来动态添加字段:
class PersonAdminForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(PersonAdminForm, self).__init__(*args, **kwargs)
new_fields = {}
for group_type in GroupType.objects.all():
field_name = "{0}_groups".format(group_type.name.lower())
qs = Group.objects.filter(group_type=group_type)
field_field = forms.ModelMultipleChoiceField(queryset=qs)
new_fields[field_name] = field_field
self.fields.update(new_fields)
class Meta:
model = Person
fields = '__all__'
就将字段添加到表单而言,这似乎可以解决问题。但是保存这些字段并将它们添加到 PersonAdmin
是行不通的。如果我将字段显式添加到 PersonAdmin
上的 fields
属性,我会得到:
FieldError: Unknown field(s) (public_groups, private_groups) specified for Person. Check fields/fieldsets/exclude attributes of class PersonAdmin.
我在尝试通过自定义 get_formsets
方法添加它们 "dynamically" 时也得到了同样的结果:
def get_fieldsets(self, request, obj=None):
fieldsets = super(PersonAdmin, self).get_fieldsets(request, obj)
print(self.form.fields)
fieldsets[0][1]['fields'].extend(['public_groups', 'private_groups'])
return fieldsets
以下代码运行良好。 ModelForm
上重写的 __init__
和 clean
方法添加动态字段并定义值的保存方式。
覆盖的 get_form
和 get_fieldsets
与 fieldsets
属性一起 AdminModel
确保动态表单字段显示在管理中。
class PersonAdminForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(PersonAdminForm, self).__init__(*args, **kwargs)
new_fields = {}
initial = self.instance.groups.all()
for group_type in GroupType.objects.all():
field_name = '{0}_groups'.format(group_type.name.lower())
qs = Group.objects.filter(group_type=group_type)
field = forms.ModelMultipleChoiceField(
queryset=qs,
required=False,
initial=initial,
)
new_fields[field_name] = field
self.fields.update(new_fields)
def clean(self):
cleaned_data = super(PersonAdminForm, self).clean()
groups = []
for group_type in GroupType.objects.all():
gt_name = '{0}_groups'.format(group_type.name.lower())
groups.extend(cleaned_data.get(gt_name))
self.instance.groups.clear()
self.instance.groups.add(*groups)
return cleaned_data
class Meta:
model = Person
fields = '__all__'
@admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
form = PersonAdminForm
# using the fieldsets attribute instead of fields in order to dynamically
# add group type fields later.
fieldsets = (
(None, {
'fields': (
'name',
),
}),
)
def get_form(self, request, obj=None, **kwargs):
kwargs['fields'] = flatten_fieldsets(self.declared_fieldsets)
return super(PersonAdmin, self).get_form(request, obj, **kwargs)
def get_fieldsets(self, request, obj=None):
fieldsets = super(PersonAdmin, self).get_fieldsets(request, obj)
newfieldsets = list(fieldsets)
fields = []
for group_type in GroupType.objects.all():
fields.append('{0}_groups'.format(group_type.name.lower()))
newfieldsets.append(['Groups', {'fields': fields}])
return newfieldsets