Django如何知道命令来呈现表单域?

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

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

我调用这个表单的一个实例的as_table()方法,Django将按照上面指定的顺序渲染这些字段。

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

(也如何重写这个顺序,例如,当我想从classe的init方法中添加一个字段?)

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

如果f是一个表单,它的字段是f.fields,它是一个django.utils.datastructures.SortedDict (它按照它们添加的顺序呈现项目)。 在表单构build之后,f.fields有一个keyOrder属性,它是一个包含字段名称的列表,它们应该被呈现。 您可以将其设置为正确的顺序(尽pipe您需要小心以确保不会遗漏项目或添加额外项目)。

以下是我刚刚在当前项目中创build的示例:

 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 

我继续回答我自己的问题。 以下是未来参考的答案:

在Django中, form.py使用__new__方法将类variables最终加载到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()))) 

新的Django 1.9是Form.field_orderForm.order_fields()

字段按照它们在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'] 

表单字段具有创build顺序的属性,名为creation_counter.fields属性是一个字典,所以添加到字典和更改所有字段中的creation_counter属性,以反映新的sorting应该足够(尽pipe从来没有尝试过)。

在Field类中使用一个计数器。 按柜台sorting:

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

使用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上的Source

从Django 1.7表单使用OrderedDict不支持追加运算符。 所以你必须从头重build字典.​​..

 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 

为了将来的参考:自newforms以来,事情已经发生了一些变化。 这是从你无法控制的基本类别中重新sorting字段的一种方法:

 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 

此外,还有一些关于base_fields在这里使用的SortedDict的文档: http : base_fields

如果任何一个fields = '__all__'

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

exclude使用:

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

然后Django引用模型中定义的字段顺序。 这只是把我抓出来,所以我想我会提到它。 它在ModelForm文档中被引用 :

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

在django 1.9窗体中定义字段最简单的方法是在你的Form.field_order窗体中使用field_order

这是一个小例子

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

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

它与用于定义表单类的元类有关。 我认为它保留了一个内部的字段列表,如果你插入列表的中间,它可能工作。 自从我看了这个代码以来,已经有一段时间了。

在内部的Meta类中使用fields是什么对我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'] 

就如此容易。

我用这个来移动字段:

 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,我相当肯定它仍然在更新的版本。