如果我有一个Django表单,例如:
class ContactForm(forms.Form):
subject = forms.CharField(max_length=100)
message = forms.CharField()
sender = forms.EmailField()
我调用此表单实例的as_table()方法,Django将按照上面指定的顺序呈现字段。
我的问题是Django如何知道定义类变量的顺序?
(另外我如何覆盖此顺序,例如当我想从classe的 init 方法添加字段时?)
答案 0 :(得分: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
答案 1 :(得分:45)
Django 1.9的新功能是 Form.field_order 和 Form.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')
fields_order = ['username', 'custom_field', 'password']
答案 2 :(得分:38)
我继续前进并回答了我自己的问题。以下是未来参考的答案:
在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())))
答案 3 :(得分: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']
答案 4 :(得分: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
中使用:Source on Github
答案 5 :(得分:5)
表单字段具有创建顺序的属性,称为creation_counter
。 .fields
属性是一个字典,如此简单地添加到字典中并在所有字段中更改creation_counter
属性以反映新的排序应该足够了(尽管从未尝试过这个)。
答案 6 :(得分: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')]
答案 7 :(得分:4)
从Django 1.7开始,表单使用OrderedDict,它不支持append运算符。所以你必须从头开始重建字典......
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
答案 8 :(得分: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
此外,还有一些关于base_fields
在此使用的SortedDict的文档:http://code.djangoproject.com/wiki/SortedDict
答案 9 :(得分: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实例最后出现。
答案 10 :(得分:2)
在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
词典中指定的顺序显示所有内容。
答案 11 :(得分: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']
就这么简单。
答案 12 :(得分:0)
它与用于定义表单类的元类有关。我认为它保留了一个内部的字段列表,如果你插入到列表的中间,它可能会工作。我看了那段代码已经有一段时间了。
答案 13 :(得分:0)
我用它来移动字段:
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,我有理由相信它仍适用于更新的版本。
答案 14 :(得分:0)
这些答案都不适合我,实际上,您不必进行任何自定义操作,您只需在 Model 类中按照您想要的顺序对字段进行排序即可。例如...下面的代码
from django.db import models
class Student(models.Model):
class Meta:
verbose_name_plural = "categories"
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=300)
nick_name = models.CharField(max_length=300)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.name
您的模型管理界面将完全按照您在本例中声明的顺序显示字段(id, name, nick_name )