Django表单、继承与字段顺序

71 投票
11 回答
36459 浏览
提问于 2025-04-15 11:51

我在我的网站上使用Django表单,想要控制字段的顺序。

这是我定义表单的方式:

class edit_form(forms.Form):
    summary = forms.CharField()
    description = forms.CharField(widget=forms.TextArea)


class create_form(edit_form):
    name = forms.CharField()

名字字段是不可变的,只在创建实体时显示。我使用继承来保持一致性和遵循DRY原则(不要重复自己)。现在发生的事情并不是错误的,实际上是完全可以预料的,就是名字字段在视图/HTML中排在最后,但我希望名字字段能放在摘要和描述的上面。我知道我可以通过把摘要和描述复制到创建表单中来轻松解决这个问题,但我想知道有没有其他方法可以做到这一点。

为什么? 想象一下,如果你在编辑表单中有100个字段,而在创建表单中需要在顶部添加10个字段——那样复制和维护这两个表单就显得不那么优雅了。(这并不是我的实际情况,我只是举个例子)

那么,我该如何覆盖这种行为呢?

编辑:

显然,没有合适的方法可以做到这一点,而不需要一些麻烦的黑科技(调整.field属性)。.field属性是一个SortedDict(Django内部的一种数据结构),它不提供重新排序键值对的方式。不过,它确实提供了一种在特定索引插入项目的方法,但这会把项目从类成员中移到构造函数里。这个方法是可行的,但会让代码变得不那么易读。我认为唯一合适的办法就是修改框架本身,但在大多数情况下这并不是最优选择。

简而言之,代码可能会变成这样:

class edit_form(forms.Form):
    summary = forms.CharField()
    description = forms.CharField(widget=forms.TextArea)


class create_form(edit_form):
    def __init__(self,*args,**kwargs):
        forms.Form.__init__(self,*args,**kwargs)

        self.fields.insert(0,'name',forms.CharField())

这让我无话可说 :)

11 个回答

12

我使用了Selene提供的解决方案,但发现它会删除所有没有被分配到keyOrder的字段。因为我正在处理的表单有很多字段,所以这个方法对我来说效果不太好。于是我根据akaihola的回答写了一个函数来解决这个问题。如果你想让它像Selene的那样工作,只需要把throw_away设置为True就可以了。

def order_fields(form, field_list, throw_away=False):
    """
    Accepts a form and a list of dictionary keys which map to the
    form's fields. After running the form's fields list will begin
    with the fields in field_list. If throw_away is set to true only
    the fields in the field_list will remain in the form.

    example use:
    field_list = ['first_name', 'last_name']
    order_fields(self, field_list)
    """
    if throw_away:
        form.fields.keyOrder = field_list
    else:
        for field in field_list[::-1]:
            form.fields.insert(0, field, form.fields.pop(field))

这是我在自己代码中使用它的方式:

class NestableCommentForm(ExtendedCommentSecurityForm):
    # TODO: Have min and max length be determined through settings.
    comment = forms.CharField(widget=forms.Textarea, max_length=100)
    parent_id = forms.IntegerField(widget=forms.HiddenInput, required=False)

    def __init__(self, *args, **kwargs):
        super(NestableCommentForm, self).__init__(*args, **kwargs)
        order_fields(self, ['comment', 'captcha'])
16

从Django 1.9开始: https://docs.djangoproject.com/en/1.10/ref/forms/api/#notes-on-field-ordering


原始回答:在Django 1.9中,表单默认会支持这个功能,使用的是 field_order

class MyForm(forms.Form):
    ...
    field_order = ['field_1', 'field_2']
    ...

https://github.com/django/django/commit/28986da4ca167ae257abcaf7caea230eca2bcd80

108

从 Django 1.9 开始

Django 1.9 新增了一个 Form 属性,叫做 field_order,这个属性可以让你按照自己想要的顺序排列表单字段,而不管你在类中声明字段的顺序是什么。

class MyForm(forms.Form):
    summary = forms.CharField()
    description = forms.CharField(widget=forms.TextArea)
    author = forms.CharField()
    notes = form.CharField()

    field_order = ['author', 'summary']

如果 field_order 中缺少的字段,它们会保持在类中的原始顺序,并且会在列表中指定的字段之后添加。上面的例子会生成以下顺序的字段: ['author', 'summary', 'description', 'notes']

详细信息可以查看文档: https://docs.djangoproject.com/en/stable/ref/forms/api/#notes-on-field-ordering

在 Django 1.6 之前

我也遇到过同样的问题,后来我在 Django CookBook 找到了另一种重新排列字段的技巧:

class EditForm(forms.Form):
    summary = forms.CharField()
    description = forms.CharField(widget=forms.TextArea)


class CreateForm(EditForm):
    name = forms.CharField()

    def __init__(self, *args, **kwargs):
        super(CreateForm, self).__init__(*args, **kwargs)
        self.fields.keyOrder = ['name', 'summary', 'description']

撰写回答