我在我的网站上使用Django表单,并希望控制字段的顺序。
以下是我定义表单的方式:
class edit_form(forms.Form):
summary = forms.CharField()
description = forms.CharField(widget=forms.TextArea)
class create_form(edit_form):
name = forms.CharField()
名称是不可变的,只应在创建实体时列出。我使用继承来增加一致性和DRY原则。事实上完全预料到的不是错误的是名称字段在视图/ html的最后列出,但我希望名称字段位于摘要和描述之上。我确实意识到我可以通过将摘要和描述复制到create_form并松散继承来轻松修复它,但我想知道这是否可行。
为什么?想象一下,你在edit_form中有100个字段,并且必须在create_form的顶部添加10个字段 - 复制和维护这两个表单看起来不那么性感。 (这是不我的情况,我只是在做一个例子)
那么,我该如何覆盖这种行为?
修改
显然,如果不经历讨厌的黑客攻击(摆弄.field属性),没有正确的方法可以做到这一点。 .field属性是SortedDict(Django的内部数据结构之一),它不提供任何重新排序键:值对的方法。它确实提供了一种在给定索引处插入项目的方法,但这会将项目从类成员移动到构造函数中。这种方法可行,但使代码可读性降低。我认为唯一合适的另一种方法是修改框架本身,这在大多数情况下都不是最佳的。
简而言之,代码会变成这样:
class edit_form(forms.Form):
summary = forms.CharField()
description = forms.CharField(widget=forms.TextArea)
class create_form(edit_form):
def __init__(self,*args,**kwargs):
forms.Form.__init__(self,*args,**kwargs)
self.fields.insert(0,'name',forms.CharField())
那让我闭嘴:)
答案 0 :(得分:98)
Django 1.9添加了一个新的Form
属性field_order
,允许对该字段进行排序,无论其在类中的声明顺序如何。
class MyForm(forms.Form):
summary = forms.CharField()
description = forms.CharField(widget=forms.TextArea)
author = forms.CharField()
notes = form.CharField()
field_order = ['author', 'summary']
field_order
中缺少的字段会在类中保留其顺序,并附加在列表中指定的字段之后。上面的示例将按以下顺序生成字段:['author', 'summary', 'description', 'notes']
请参阅文档:https://docs.djangoproject.com/en/stable/ref/forms/api/#notes-on-field-ordering
我遇到了同样的问题,我找到了另一种重新排序Django CookBook字段的技术:
class EditForm(forms.Form):
summary = forms.CharField()
description = forms.CharField(widget=forms.TextArea)
class CreateForm(EditForm):
name = forms.CharField()
def __init__(self, *args, **kwargs):
super(CreateForm, self).__init__(*args, **kwargs)
self.fields.keyOrder = ['name', 'summary', 'description']
答案 1 :(得分:16)
来自Django 1.9:https://docs.djangoproject.com/en/1.10/ref/forms/api/#notes-on-field-ordering
原始回答: Django 1.9默认会在field_order
的表单上支持此功能:
class MyForm(forms.Form):
...
field_order = ['field_1', 'field_2']
...
https://github.com/django/django/commit/28986da4ca167ae257abcaf7caea230eca2bcd80
答案 2 :(得分:11)
我使用了Selene发布的解决方案,但发现它删除了所有未分配给keyOrder的字段。我正在子类化的表单有很多字段,所以这对我来说效果不好。我使用akaihola的答案编写了这个函数以解决问题,但是如果你希望它像Selene那样工作,你需要做的就是将throw_away
设置为True
。
def order_fields(form, field_list, throw_away=False):
"""
Accepts a form and a list of dictionary keys which map to the
form's fields. After running the form's fields list will begin
with the fields in field_list. If throw_away is set to true only
the fields in the field_list will remain in the form.
example use:
field_list = ['first_name', 'last_name']
order_fields(self, field_list)
"""
if throw_away:
form.fields.keyOrder = field_list
else:
for field in field_list[::-1]:
form.fields.insert(0, field, form.fields.pop(field))
这就是我在自己的代码中使用它的方式:
class NestableCommentForm(ExtendedCommentSecurityForm):
# TODO: Have min and max length be determined through settings.
comment = forms.CharField(widget=forms.Textarea, max_length=100)
parent_id = forms.IntegerField(widget=forms.HiddenInput, required=False)
def __init__(self, *args, **kwargs):
super(NestableCommentForm, self).__init__(*args, **kwargs)
order_fields(self, ['comment', 'captcha'])
答案 3 :(得分:10)
似乎在某些时候,字段顺序的基础结构已从django特定SordedDict
更改为python标准OrderedDict
因此,在1.7中我必须做以下事情:
from collections import OrderedDict
class MyForm(forms.Form):
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
original_fields = self.fields
new_order = OrderedDict()
for key in ['first', 'second', ... 'last']:
new_order[key] = original_fields[key]
self.fields = new_order
我敢肯定有人可以把它打成两三线,但对于S.O.目的我清楚地表明它的工作原理比切割器更好。
答案 4 :(得分:7)
你也可以创建一个装饰器来订购字段(灵感来自Joshua的解决方案):
def order_fields(*field_list):
def decorator(form):
original_init = form.__init__
def init(self, *args, **kwargs):
original_init(self, *args, **kwargs)
for field in field_list[::-1]:
self.fields.insert(0, field, self.fields.pop(field))
form.__init__ = init
return form
return decorator
这将确保传递给装饰器的所有字段都是第一个。 您可以像这样使用它:
@order_fields('name')
class CreateForm(EditForm):
name = forms.CharField()
答案 5 :(得分:6)
接受的答案的方法是使用在Django 1.7中更改的内部Django表单API。 The project team's opinion is that it should never have been used in the first place.我现在使用此函数重新排序表单。此代码使用OrderedDict
:
def reorder_fields(fields, order):
"""Reorder form fields by order, removing items not in order.
>>> reorder_fields(
... OrderedDict([('a', 1), ('b', 2), ('c', 3)]),
... ['b', 'c', 'a'])
OrderedDict([('b', 2), ('c', 3), ('a', 1)])
"""
for key, v in fields.items():
if key not in order:
del fields[key]
return OrderedDict(sorted(fields.items(), key=lambda k: order.index(k[0])))
我在这样的课程中使用它:
class ChangeOpenQuestion(ChangeMultipleChoice):
def __init__(self, *args, **kwargs):
super(ChangeOpenQuestion, self).__init__(*args, **kwargs)
key_order = ['title',
'question',
'answer',
'correct_answer',
'incorrect_answer']
self.fields = reorder_fields(self.fields, key_order)
答案 6 :(得分:3)
请参阅this SO question中有关Django内部跟踪字段顺序的说明;答案包括如何根据自己的喜好“重新排序”字段的建议(最后归结为弄乱.fields
属性)。
答案 7 :(得分:2)
更改字段顺序的替代方法:
流行音乐和插入:
self.fields.insert(0, 'name', self.fields.pop('name'))
弹出和追加:
self.fields['summary'] = self.fields.pop('summary')
self.fields['description'] = self.fields.pop('description')
弹出和追加全:
for key in ('name', 'summary', 'description'):
self.fields[key] = self.fields.pop(key)
有序副本:
self.fields = SortedDict( [ (key, self.fields[key])
for key in ('name', 'summary' ,'description') ] )
但Selene的Django CookBook方法仍然让人感觉最清楚。
答案 8 :(得分:1)
基于@akaihola的回答并更新为使用最新的Django 1.5 self.fields.insert
正在depreciated。
from easycontactus.forms import *
from django import forms
class CustomEasyContactUsForm(EasyContactUsForm):
### form settings and configuration
CAPTHCA_PHRASE = 'igolf'
### native methods
def __init__(self, *args, **kwargs):
super(CustomEasyContactUsForm, self).__init__(*args, **kwargs)
# re-order placement of added attachment field
self.fields.keyOrder.insert(self.fields.keyOrder.index('captcha'),
self.fields.keyOrder.pop(self.fields.keyOrder.index('attachment'))
)
### field defintitions
attachment = forms.FileField()
在上面我们正在扩展一个EasyContactUsForm基类,因为它在django-easycontactus包中定义。
答案 9 :(得分:0)
我从Django-Registration-Redux中创建了一个继承自'RegistrationForm'的表单'ExRegistrationForm'。我遇到了两个问题,其中一个是在创建新表单后重新排序html输出页面上的字段。
我按如下方式解决了这些问题:
<强> 1。问题1:从注册表中删除用户名:在my_app.forms.py
class ExRegistrationForm(RegistrationForm):
#Below 2 lines extend the RegistrationForm with 2 new fields firstname & lastname
first_name = forms.CharField(label=(u'First Name'))
last_name = forms.CharField(label=(u'Last Name'))
#Below 3 lines removes the username from the fields shown in the output of the this form
def __init__(self, *args, **kwargs):
super(ExRegistrationForm, self).__init__(*args, **kwargs)
self.fields.pop('username')
<强> 2。问题2:使FirstName和LastName出现在顶部:在templates / registration / registration_form.html
您可以按所需顺序单独显示字段。如果字段数量较少,这将有所帮助,但如果您有大量字段,实际上不可能在表单中实际写入它们,则会有所帮助。
{% extends "base.html" %}
{% load i18n %}
{% block content %}
<form method="post" action=".">
{% csrf_token %}
#The Default html is: {{ form.as_p }} , which can be broken down into individual elements as below for re-ordering.
<p>First Name: {{ form.first_name }}</p>
<p>Last Name: {{ form.last_name }}</p>
<p>Email: {{ form.email }}</p>
<p>Password: {{ form.password1 }}</p>
<p>Confirm Password: {{ form.password2 }}</p>
<input type="submit" value="{% trans 'Submit' %}" />
</form>
{% endblock %}
答案 10 :(得分:0)
以上答案正确但不完整。仅当所有字段都定义为类变量时,它们才起作用。那么必须在初始化器(__init__
)中定义的动态表单字段呢?
from django import forms
class MyForm(forms.Form):
field1 = ...
field2 = ...
field_order = ['val', 'field1', 'field2']
def __init__(self, val_list, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
vals = zip(val_list, val_list)
self.fields['val'] = forms.CharField(choices=vals)
以上内容将不适用于val
,但将适用于field1
和field2
(如果我们重新排序)。您可能要尝试在初始化程序中定义field_order
:
class MyForm(forms.Form):
# other fields
def __init__(self, val_list, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
vals = zip(val_list, val_list)
self.fields['val'] = forms.CharField(choices=vals)
self.field_order = ['val', 'field1', 'field2']
,但这也会失败,因为 在调用super()
之前 字段顺序是固定的。
因此,唯一的解决方案是构造函数(__new__
)并将field_order
设置为类变量。
class MyForm(forms.Form):
# other fields
field_order = ['val', 'field1', 'field2']
def __new__(cls, val_list, *args, **kwargs):
form = super(MyForm, cls).__new__(cls)
vals = zip(val_list, val_list)
form.base_fields['val'] = forms.CharField(choices=vals)
return form