如何在ModelAdmin中动态操作Django表单字段?

4 投票
3 回答
4620 浏览
提问于 2025-04-16 18:05

我有一个字段(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 个回答

1

在你的 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)

2

我想分享一下我的经验,希望对其他人有帮助。

我在 get_form 方法中一直无法设置 form.fields['slug'].required,也不知道为什么会这样。不过,我通过创建一个新的表单,继承自 ModelForm,解决了我的问题。

我需要重写 init() 方法,在调用父类的构造函数之后,设置 self.fields['slug'].required = False,然后重写 clean_slug() 方法,以便在需要时通过 self.data['slug'] 来修改 slug 字段的内容。

希望这对某些人有帮助。

5

我在谷歌上找到这个页面,因为我自己也在解决同样的问题。下面的内容在 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 的解决方案会更好。

撰写回答