Django如何知道渲染表单字段的顺序?

107 投票
17 回答
56420 浏览
提问于 2025-04-11 20:22

如果我有一个Django表单,比如:

class ContactForm(forms.Form):
    subject = forms.CharField(max_length=100)
    message = forms.CharField()
    sender = forms.EmailField()

当我调用这个表单实例的as_table()方法时,Django会按照上面指定的顺序来显示这些字段。

我想知道Django是怎么知道这些类变量的定义顺序的?

(另外,我该如何改变这个顺序,比如当我想在类的init方法中添加一个字段时?)

17 个回答

90

[注意:这个回答现在已经过时了 - 请查看下面的讨论和更新的回答]。

如果 f 是一个表单,它的字段可以通过 f.fields 来访问,这个字段是一个 django.utils.datastructures.SortedDict 类型(它会按照添加的顺序来展示项目)。在表单构建完成后,f.fields 会有一个叫做 keyOrder 的属性,这个属性是一个列表,里面包含了字段名称,按照应该展示的顺序排列。你可以把这个顺序设置成正确的顺序(不过要小心,确保不要漏掉任何项目或多加项目)。

下面是我在当前项目中创建的一个例子:

class PrivEdit(ModelForm):
    def __init__(self, *args, **kw):
        super(ModelForm, self).__init__(*args, **kw)
        self.fields.keyOrder = [
            'super_user',
            'all_districts',
            'multi_district',
            'all_schools',
            'manage_users',
            'direct_login',
            'student_detail',
            'license']
    class Meta:
        model = Privilege
103

Django 1.9 新增了两个功能:Form.field_orderForm.order_fields()

# forms.Form example
class SignupForm(forms.Form):

    password = ...
    email = ...
    username = ...

    field_order = ['username', 'email', 'password']


# forms.ModelForm example
class UserAccount(forms.ModelForm):

    custom_field = models.CharField(max_length=254)

    def Meta:
        model = User
        fields = ('username', 'email')

    field_order = ['username', 'custom_field', 'password']
40

我自己回答了自己的问题。为了将来参考,这里是答案:

在Django中,form.py 通过使用 __new__ 方法做了一些神奇的事情,把你的类变量加载到 self.fields 中,并按照类中定义的顺序排列。self.fields 是一个Django的 SortedDict 实例(在 datastructures.py 中定义)。

所以如果你想要改变这个顺序,比如在我的例子中想让 sender 先出现,但又需要在 init 方法中添加它,你可以这样做:

class ContactForm(forms.Form):
    subject = forms.CharField(max_length=100)
    message = forms.CharField()
    def __init__(self,*args,**kwargs):
        forms.Form.__init__(self,*args,**kwargs)
        #first argument, index is the position of the field you want it to come before
        self.fields.insert(0,'sender',forms.EmailField(initial=str(time.time())))

撰写回答