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