如何在ModelAdmin中动态操作Django表单字段?
我有一个字段(slug),在模型中是“必填”的,但我想在ModelAdmin类中把这个字段改成可选的。如果用户不填写这个字段,它会自动用另一个字段(name)的内容来填充。
class SomeModel(model.Model):
name = model.CharField(max_length=255)
slug = model.SlugField(unique=True, max_length=255)
我尝试了好几种方法,比如在ModelAdmin中重写get_form(),或者使用ModelForm类并专门指定表单。
class SomeModelAdmin(admin.ModelAdmin):
def get_form(self, request, obj=None, **kwargs):
form = super(self.__class__, self).get_form(request, obj, **kwargs)
form.slug.required = False
return form
但是,这两种方法都没有成功。除了手动创建表单,还有没有其他更快的解决办法?
我有很多这样的表单,手动处理可能会很麻烦,也不容易维护。
3 个回答
在你的 get_form
方法里,form.fields['slug'].required
应该是可以用的。
不过,正确的做法是直接提供一个自定义的 ModelForm。
class SomeModelForm(forms.ModelForm):
slug = forms.CharField(required=False)
class SomeModelAdmin(admin.ModelAdmin):
form = SomeModelForm
顺便说一下,请不要使用 super(self.__class__, self)
。使用 super
时,应该明确写出当前的类名,否则任何从你这个类继承的子类在调用 super
时会出问题。
编辑 时要修改 form.fields
,而不是 forms.fields
。
当你写 self.__class__
时,你实际上是在阻止 Python 理解继承关系——因为它总是指向具体的类,也就是继承树的最底层。但如果你的方法在继承树的中间,那么在 super
中引用具体的类就是错误的——因为你想要调用的是你所在层级的上一级,而不是最底层的上一级。这就是为什么你总是应该写出你所在的类名——在这个例子中,就是 super(SomeModelAdmin, self)
。
我想分享一下我的经验,希望对其他人有帮助。
我在 get_form
方法中一直无法设置 form.fields['slug'].required
,也不知道为什么会这样。不过,我通过创建一个新的表单,继承自 ModelForm,解决了我的问题。
我需要重写 init() 方法,在调用父类的构造函数之后,设置 self.fields['slug'].required = False
,然后重写 clean_slug()
方法,以便在需要时通过 self.data['slug']
来修改 slug 字段的内容。
希望这对某些人有帮助。
我在谷歌上找到这个页面,因为我自己也在解决同样的问题。下面的内容在 ModelAdmin
中也可以使用:
def get_form(self, *args, **kwargs):
form = super(SomeModelAdmin, self).get_form(*args, **kwargs)
form.base_fields['slug'].required = False
return form
从更新后的 ModelFormMetaclass
创建的后续表单将会把 slug
字段设置为可选。
在我的情况下,这种方法更有效,因为我只有一个类需要把这个字段设为可选,并且在保存时不需要对数据进行任何转换。如果你有很多类,或者需要进行数据转换,GoogleDroid 的解决方案会更好。