Django如何知道呈现表单字段的顺序?


89

如果我有Django表单,例如:

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

我调用了这种形式的实例的as_table()方法,Django将按照上述指定的顺序渲染字段。

我的问题是Django如何知道定义类变量的顺序?

(例如,当我想从类的init方法中添加字段时,如何覆盖此顺序?)

Answers:


89

[注意:这个答案现在已经完全过时了-请参阅下面的讨论以及更多最新答案]。

如果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

19
在一段时间内,字段将按照ModelForm的'fields'属性指定的顺序排序。从 docs.djangoproject.com/en/1.3/topics/forms/modelforms/…:表单呈现它们时,将遵循在该列表中指定字段名称的顺序。这仅适用于ModelForms-您的方法对于常规表单仍然非常有用,尤其是在添加其他字段的表单子类中。
克里斯·劳洛

3
当您在做一些覆盖某些字段的时髦内容时,此方法有效。+1
内森·凯勒

“这”是Chris Lawlor的建议吗?这个答案已经够老了,可能不再适用了(但对于1.2来说可能是好的)。确保标记不可靠的信息很重要,否则新来者不知道该信任什么。感谢您的输入。
holdenweb

67

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']

1
这是我正在寻找的答案!
sivabudh

1
从1.9开始,这应该是新搜索者应该关注的答案,显然仍然可以在1.10中使用
Brian H.

2
注意:不是“ fields_order”而是“ field_order”(否)
Davy

1
您甚至可以只指定要订购的表单字段的子集
danleyb2,

40

我继续回答自己的问题。这是供将来参考的答案:

在Django form.py中,使用该__new__方法将类变量最终self.fields按类中定义的顺序加载到其中,这确实有些不可思议。 self.fields是Django SortedDict实例(在中定义datastructures.py)。

因此,要覆盖此问题,请在我的示例中说,您希望发送方优先,但需要将其添加到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())))

12
您可以在顶部正常定义“发件人”字段,然后在插入行中使用变体:self.fields.insert( 0, 'sender', self.fields['sender'] )
EvdB 2011年

4
这在Django 1.7中不再起作用,因为表单字段使用不支持附加的OrderedDict。请参阅下面的更新后的答案(等待评论的时间太长)...
Paul Kenjora 2015年

11

字段按照在ModelClass._meta.fields中定义的顺序列出。但是,如果要更改Form中的顺序,可以使用keyOrder函数来完成。例如 :

class ContestForm(ModelForm):
  class Meta:
    model = Contest
    exclude=('create_date', 'company')

  def __init__(self, *args, **kwargs):
    super(ContestForm, self).__init__(*args, **kwargs)
    self.fields.keyOrder = [
        'name',
        'description',
        'image',
        'video_link',
        'category']

6

使用Django> = 1.7时,您必须ContactForm.base_fields进行如下修改:

from collections import OrderedDict

...

class ContactForm(forms.Form):
    ...

ContactForm.base_fields = OrderedDict(
    (k, ContactForm.base_fields[k])
    for k in ['your', 'field', 'in', 'order']
)

这个技巧在Django Admin中使用PasswordChangeForm来自Github的源代码


3
有趣的是,在试图弄清子类化为什么PasswordChangeForm改变了字段的顺序时,我寻求了字段的顺序,因此您的示例对我非常有用。似乎是因为base_fields没有继承到子类。解决方案似乎是PasswordChangeFormSubclass.base_fields = PasswordChangeForm.base_fields在定义`PasswordChangeFormSubclass
orblivion

@orblivion:ContactForm.base_fields[k]在构建有序字典时使用。答案有误,我将编辑
blueFast 2015年

5

表单字段具有创建顺序的属性,称为creation_counter.fieldsattribute是一个字典,因此只需简单地添加到字典中并更改creation_counter所有字段中的属性以反映新的顺序就足够了(尽管不要尝试过)。


5

在Field类中使用一个计数器。按该计数器排序:

import operator
import itertools

class Field(object):
    _counter = itertools.count()
    def __init__(self):
        self.count = Field._counter.next()
        self.name = ''
    def __repr__(self):
        return "Field(%r)" % self.name

class MyForm(object):
    b = Field()
    a = Field()
    c = Field()

    def __init__(self):
        self.fields = []
        for field_name in dir(self):
            field = getattr(self, field_name)
            if isinstance(field, Field):
                field.name = field_name
                self.fields.append(field)
        self.fields.sort(key=operator.attrgetter('count'))

m = MyForm()
print m.fields # in defined order

输出:

[Field('b'), Field('a'), Field('c')]


4

从Django 1.7版本开始,请使用不支持append运算符的OrderedDict。因此,您必须从头开始重建字典.​​..

class ChecklistForm(forms.ModelForm):

  class Meta:
    model = Checklist
    fields = ['name', 'email', 'website']

  def __init__(self, guide, *args, **kwargs):
    self.guide = guide
    super(ChecklistForm, self).__init__(*args, **kwargs)

    new_fields = OrderedDict()
    for tier, tasks in guide.tiers().items():
      questions = [(t['task'], t['question']) for t in tasks if 'question' in t]
      new_fields[tier.lower()] = forms.MultipleChoiceField(
        label=tier,
        widget=forms.CheckboxSelectMultiple(),
        choices=questions,
        help_text='desired set of site features'
      )

    new_fields['name'] = self.fields['name']
    new_fields['email'] = self.fields['email']
    new_fields['website'] = self.fields['website']
    self.fields = new_fields 

从python 3.2开始,您可以使用OrderedDict.move_to_end()前置或追加项目。
bparker

3

供将来参考:自从新表格出现以来,情况有所改变。这是您无法控制的基本表单类中的字段重新排序的一种方法:

def move_field_before(form, field, before_field):
    content = form.base_fields[field]
    del(form.base_fields[field])
    insert_at = list(form.base_fields).index(before_field)
    form.base_fields.insert(insert_at, field, content)
    return form

另外,这里还有一些有关SortedDict的文档base_fieldshttp : //code.djangoproject.com/wiki/SortedDict


在班级中使用Metas为我工作(使用ModelForm),直到我有理由使用MyFormSet.form.base_fields为外键字段设置初始值为止,此时“ fields”中的顺序为no不再受尊重。我的解决方案实际上只是对基础模型中的字段进行重新排序。
Paul J

2

如果有fields = '__all__'

class AuthorForm(ModelForm):
    class Meta:
        model = Author
        fields = '__all__'

exclude用于:

class PartialAuthorForm(ModelForm):
    class Meta:
        model = Author
        exclude = ['title']

然后Django引用模型中定义的字段顺序。这只是吸引我,所以我想提一提。在ModelForm docs中引用了它:

如果使用这两种方法中的任何一种,则字段在表单中出现的顺序将是字段在模型中定义的顺序,其中ManyToManyField实例最后出现。


2

在Django 1.9表单中订购字段的最简单方法是field_order在表单Form.field_order中使用

这是一个小例子

class ContactForm(forms.Form):
     subject = forms.CharField(max_length=100)
     message = forms.CharField()
     sender = forms.EmailField()
     field_order = ['sender','message','subject']

这将按照您在field_orderdict中指定的顺序显示所有内容。


1

fields在内部Meta类中使用对我有效的是Django==1.6.5

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
Example form declaration with custom field order.
"""

from django import forms

from app.models import AppModel


class ExampleModelForm(forms.ModelForm):
    """
    An example model form for ``AppModel``.
    """
    field1 = forms.CharField()
    field2 = forms.CharField()

    class Meta:
        model = AppModel
        fields = ['field2', 'field1']

就如此容易。


1
似乎此方法仅对模型形式有效,正常形式django.forms不起作用
Pengfei.X 2014年

1

我用它来移动字段:

def move_field_before(frm, field_name, before_name):
    fld = frm.fields.pop(field_name)
    pos = frm.fields.keys().index(before_name)
    frm.fields.insert(pos, field_name, fld)

这可以在1.5版中使用,我可以肯定地说,它仍然可以在最新版本中使用。


0

它与用于定义表单类的元类有关。我认为它保留了字段的内部列表,如果您将其插入列表的中间,则可能会起作用。自从我看过这段代码已经有一段时间了。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.