我希望有一个关于一个字段值的其他字段。因此,我构建了一个自定义管理表单来添加一些新字段。
与jacobian 1的博客相关,这就是我想出的:
class ProductAdminForm(forms.ModelForm):
class Meta:
model = Product
def __init__(self, *args, **kwargs):
super(ProductAdminForm, self).__init__(*args, **kwargs)
self.fields['foo'] = forms.IntegerField(label="foo")
class ProductAdmin(admin.ModelAdmin):
form = ProductAdminForm
admin.site.register(Product, ProductAdmin)
但附加字段'foo'未显示在管理员中。如果我添加这样的字段,一切正常,但不如所需的动态,添加关于模型的另一个字段的值的字段
class ProductAdminForm(forms.ModelForm):
foo = forms.IntegerField(label="foo")
class Meta:
model = Product
class ProductAdmin(admin.ModelAdmin):
form = ProductAdminForm
admin.site.register(Product, ProductAdmin)
那么有什么初始化方法我必须再次触发才能使新字段工作?或者还有其他尝试吗?
答案 0 :(得分:16)
这是问题的解决方案。感谢koniiiik,我试图通过扩展* get_fieldsets *方法
来解决这个问题class ProductAdmin(admin.ModelAdmin):
def get_fieldsets(self, request, obj=None):
fieldsets = super(ProductAdmin, self).get_fieldsets(request, obj)
fieldsets[0][1]['fields'] += ['foo']
return fieldsets
如果使用多个字段集,请务必使用适当的索引将其添加到右侧字段集。
答案 1 :(得分:6)
这适用于在Django 1.9.3中添加动态字段,仅使用ModelAdmin类(无ModelForm)和覆盖get_fields
。我不知道它有多强大:
class MyModelAdmin(admin.ModelAdmin):
fields = [('title','status', ), 'description', 'contact_person',]
exclude = ['material']
def get_fields(self, request, obj=None):
gf = super(MyModelAdmin, self).get_fields(request, obj)
new_dynamic_fields = [
('test1', forms.CharField()),
('test2', forms.ModelMultipleChoiceField(MyModel.objects.all(), widget=forms.CheckboxSelectMultiple)),
]
#without updating get_fields, the admin form will display w/o any new fields
#without updating base_fields or declared_fields, django will throw an error: django.core.exceptions.FieldError: Unknown field(s) (test) specified for MyModel. Check fields/fieldsets/exclude attributes of class MyModelAdmin.
for f in new_dynamic_fields:
#`gf.append(f[0])` results in multiple instances of the new fields
gf = gf + [f[0]]
#updating base_fields seems to have the same effect
self.form.declared_fields.update({f[0]:f[1]})
return gf
答案 2 :(得分:5)
虽然雅各布的帖子可能适用于常规ModelForm
(即使它超过一年半),但管理员却有点不同。
定义模型的所有声明方式,形式ModelAdmins和诸如此类的东西大量使用元类和类内省。与管理员相同 - 当您告诉ModelAdmin
使用特定表单而不是创建默认表单时,它会内省类。它从类本身获取字段列表和其他内容,而不实例化它。
但是,您的自定义类没有在类级别定义额外的表单字段,而是在实例化之后动态添加一个 - 这对于ModelAdmin
来说为时已晚这种变化。
解决问题的一种方法可能是继承ModelAdmin
并覆盖其get_fieldsets
方法以实际实例化ModelForm
类并从实例中获取字段列表而不是类。但是,您必须记住,这可能比默认实现慢一些。
答案 3 :(得分:5)
上面接受的答案适用于旧版本的django,这就是我的做法。这已经在以后的django版本中打破了(我目前在1.68上,但即使现在已经老了)。
它现在被破坏的原因是因为你从ModelAdmin.get_fieldsets()返回的字段集中的任何字段最终都作为fields =参数传递给了modelform_factory(),这会给你一个错误,因为列表中的字段不是存在(并且在实例化表单并调用其__ init __之前不会存在。)
为了解决这个问题,我们必须覆盖ModelAdmin.get_form()并提供一个字段列表,其中不包含将在以后添加的任何额外字段。 get_form的默认行为是为此信息调用get_fieldsets(),我们必须防止这种情况发生:
# CHOOSE ONE
# newer versions of django use this
from django.contrib.admin.utils import flatten_fieldsets
# if above does not work, use this
from django.contrib.admin.util import flatten_fieldsets
class MyModelForm(ModelForm):
def __init__(self, *args, **kwargs):
super(MyModelForm, self).__init__(*args, **kwargs)
# add your dynamic fields here..
for fieldname in ('foo', 'bar', 'baz',):
self.fields[fieldname] = form.CharField()
class MyAdmin(ModelAdmin):
form = MyModelForm
fieldsets = [
# here you put the list of fieldsets you want displayed.. only
# including the ones that are not dynamic
]
def get_form(self, request, obj=None, **kwargs):
# By passing 'fields', we prevent ModelAdmin.get_form from
# looking up the fields itself by calling self.get_fieldsets()
# If you do not do this you will get an error from
# modelform_factory complaining about non-existent fields.
# use this line only for django before 1.9 (but after 1.5??)
kwargs['fields'] = flatten_fieldsets(self.declared_fieldsets)
# use this line only for django 1.9 and later
kwargs['fields'] = flatten_fieldsets(self.fieldsets)
return super(MyAdmin, self).get_form(request, obj, **kwargs)
def get_fieldsets(self, request, obj=None):
fieldsets = super(MyAdmin, self).get_fieldsets(request, obj)
newfieldsets = list(fieldsets)
fields = ['foo', 'bar', 'baz']
newfieldsets.append(['Dynamic Fields', { 'fields': fields }])
return newfieldsets
答案 4 :(得分:4)
您可以使用表单元类创建动态字段和字段集。示例代码如下。根据您的要求添加循环逻辑。
class CustomAdminFormMetaClass(ModelFormMetaclass):
"""
Metaclass for custom admin form with dynamic field
"""
def __new__(cls, name, bases, attrs):
for field in get_dynamic_fields: #add logic to get the fields
attrs[field] = forms.CharField(max_length=30) #add logic to the form field
return super(CustomAdminFormMetaClass, cls).__new__(cls, name, bases, attrs)
class CustomAdminForm(six.with_metaclass(CustomAdminFormMetaClass, forms.ModelForm)):
"""
Custom admin form
"""
class Meta:
model = ModelName
fields = "__all__"
class CustomAdmin(admin.ModelAdmin):
"""
Custom admin
"""
fieldsets = None
form = CustomAdminForm
def get_fieldsets(self, request, obj=None):
"""
Different fieldset for the admin form
"""
self.fieldsets = self.dynamic_fieldset(). #add logic to add the dynamic fieldset with fields
return super(CustomAdmin, self).get_fieldsets(request, obj)
def dynamic_fieldset(self):
"""
get the dynamic field sets
"""
fieldsets = []
for group in get_field_set_groups: #logic to get the field set group
fields = []
for field in get_group_fields: #logic to get the group fields
fields.append(field)
fieldset_values = {"fields": tuple(fields), "classes": ['collapse']}
fieldsets.append((group, fieldset_values))
fieldsets = tuple(fieldsets)
return fieldsets
答案 5 :(得分:2)
斯蒂芬的回答很优雅,但是当我在dj1.6中使用时,它要求该字段为元组。 完整的解决方案如下所示:
class ProductForm(ModelForm):
foo = CharField(label='foo')
class ProductAdmin(admin.ModelAdmin):
form = ProductForm
def get_fieldsets(self, request, obj=None):
fieldsets = super(ProductAdmin, self).get_fieldsets(request, obj)
fieldsets[0][1]['fields'] += ('foo', )
return fieldsets
答案 6 :(得分:1)
也许我来晚了...但是,我使用的是Django 3.0,并且还希望根据请求将一些自定义字段动态添加到表单中。
我最终得到了一种类似于@tehfink和@little_birdie所述的解决方案。
但是,仅按照建议更新self.form.declared_fields
并没有帮助。此过程的结果是,self.form.declared_fields
中定义的自定义字段列表始终随请求而增长。
我通过首先初始化此字典来解决此问题:
class ModelAdminGetCustomFieldsMixin(object):
def get_fields(self, request, obj=None):
fields = super().get_fields(request, obj=None)
self.form.declared_fields = {}
if obj:
for custom_attribute in custom_attribute_list:
self.form.declared_fields.update({custom_attribute.name: custom_attribute.field})
return fields
其中custom_attribute.field
是一个表单字段实例。
此外,还需要定义一个ModelForm,其中在初始化过程中还动态添加了自定义字段:
class SomeModelForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for custom_attribute in custom_attribute_list:
self.fields[custom_attribute.name] = custom_attribute.field
并在ModelAdmin中使用此ModelForm。
此后,可以在例如字段集中使用新定义的属性。
答案 7 :(得分:0)
不确定为什么不起作用,但是可能的解决办法是静态地(在表单上)定义字段,然后在__init__
中覆盖它?
答案 8 :(得分:0)
我很长时间无法解决动态添加字段的问题。 解决方案“little_birdie”确实有效。谢谢Birdie)) 唯一的细微差别是: “Self.declared_fieldsets”应替换为“self.fieldsets”。
#kwargs['fields'] = flatten_fieldsets(self.declared_fieldsets)
kwargs['fields'] = flatten_fieldsets(self.fieldsets)
我使用的是版本1.10。也许有些事情发生了变化。
如果有人找到更简单优雅的解决方案,请点击此处。
感谢所有人)))