Djangoforms,inheritance和表单字段的顺序

我在我的网站中使用Django表单,并希望控制字段的顺序。

以下是我如何定义我的表单:

class edit_form(forms.Form): summary = forms.CharField() description = forms.CharField(widget=forms.TextArea) class create_form(edit_form): name = forms.CharField() 

该名称是不可变的,只应在实体创build时列出。 我使用inheritance来增加一致性和DRY原则。 事实上完全可以预见的是,没有错误的是,名称字段在view / html中最后列出,但是我希望名称字段位于摘要和描述之上。 我意识到,我可以通过将摘要和描述复制到create_form中,并松开inheritance来轻松修复它,但是我想知道这是否可能。

为什么? 想象一下,在edit_form中有100个字段,并且在create_form的顶部必须添加10个字段 – 复制和维护这两个表单看起来并不那么性感。 (这不是我的情况,我只是做一个例子)

那么,我该如何重写这个行为呢?

编辑:

显然没有经过讨厌的黑客(摆弄.field属性)没有正确的方法来做到这一点。 .field属性是一个SortedDict(Django的内部数据结构之一),它不提供任何重新sorting键值对的方法。 它确实提供了一种在给定索引处插入项目的方法,但是这会将项目从类成员移动到构造器中。 这个方法可以工作,但是使代码更不可读。 我认为唯一的另一种方式是修改框架本身,这在大多数情况下是不太理想的。

总之,代码会变成这样的:

 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()) 

那让我闭嘴:)

我有这个相同的问题,我发现了另一种重新sortingDjango 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'] 

从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

我使用了Selene发布的解决scheme,但发现它删除了所有未分配给keyOrder的字段。 我inheritance的forms有很多领域,所以这不适合我的工作。 我编写了这个函数来解决这个问题,使用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']) 

看起来在某种程度上,字段顺序的基本结构从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 

我敢肯定有人可以把它打成两三行,但是为了达到目的,我想清楚地说明它是如何工作的,比切肉刀好。

您也可以创build一个装饰器来订购字段(受约书亚解决scheme的启发):

 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() 

接受的答案的方法使用Django 1.7中更改的内部Django表单API。 项目组的意见是,它应该从来没有被用在首位。 我现在使用这个函数来重新sorting我的表单。 这段代码使用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) 

在Django内部跟踪字段顺序的过程中,看到这个SO问题中的注释; 答案包括如何根据自己的喜好“重新sorting”字段的build议(最后归结为与.fields属性相混淆)。

replace字段顺序的方法:

stream行和插入:

 self.fields.insert(0, 'name', self.fields.pop('name')) 

popup式和追加:

 self.fields['summary'] = self.fields.pop('summary') self.fields['description'] = self.fields.pop('description') 

popup式和追加全:

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

但是,来自Django CookBook的Selene的方法仍然感觉最清晰。

基于@akaihola的答案,并更新到最新的Django 1.5作为self.fields.insert正在折旧 。

 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包中定义的那样 。

我从Django-Registration-Redux的“RegistrationForm”构build了一个“ExRegistrationForm”表单。 我遇到了两个问题,其中一个是在创build新表单后重新排列html输出页面上的字段。

我解决他们如下:

1.问题1:从registry中删除用户名:在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:使名字和姓氏显示在顶部:在templates / registration / registration_form.html中

您可以单独显示所需的顺序字段。 如果字段数量较less,这将有所帮助,但是如果您有大量的字段,实际上不可能在表单中实际编写字段,则这不会有帮助。

  {% 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 %}