100

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

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

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

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

(还有我如何覆盖这个顺序,例如当我想从类的init方法中添加一个字段时?)

4

16 回答 16

95

Django 1.9 的新功能是Form.field_orderForm.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')

    field_order = ['username', 'custom_field', 'password']
于 2015-12-28T23:05:23.590 回答
90

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

如果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
于 2009-07-28T00:12:20.463 回答
40

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

在 Djangoform.py中,使用该方法执行一些黑魔法,__new__最终self.fields按照类中定义的顺序将类变量加载到其中。 self.fields是一个 DjangoSortedDict实例(定义在 中datastructures.py)。

所以要覆盖它,比如在我的示例中,您希望 sender 首先出现,但需要将其添加到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())))
于 2008-12-08T21:12:56.517 回答
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']
于 2011-04-21T16:53:43.070 回答
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 中使用PasswordChangeFormGithub 上的源代码

于 2014-12-22T00:13:52.920 回答
5

表单字段有一个用于创建顺序的属性,称为creation_counter. .fields属性是一个字典,因此简单地添加到字典并更改creation_counter所有字段中的属性以反映新的顺序就足够了(尽管从未尝试过)。

于 2008-12-08T20:52:25.027 回答
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')]

于 2009-05-15T22:03:32.970 回答
4

从 Django 1.7 开始,表单使用不支持附加运算符的 OrderedDict。所以你必须从头开始重建字典.​​.....

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 
于 2015-03-03T22:18:34.967 回答
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

于 2010-03-31T09:53:37.127 回答
3

如果是fields = '__all__'

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

或被exclude使用:

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

然后 Django 引用模型中定义的字段顺序。这让我很生气,所以我想我会提到它。它在ModelForm 文档中被引用:

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

于 2014-12-05T18:19:05.613 回答
2

在 django 1.9 表单中排序字段的最简单方法是在表单中field_order使用Form.field_order

这是一个小例子

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

这将按照您在field_orderdict.xml 中指定的顺序显示所有内容。

于 2016-09-09T13:17:11.593 回答
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']

就如此容易。

于 2014-08-22T14:14:27.560 回答
1

我用它来移动字段:

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 中有效,我有理由确定它在更新的版本中仍然有效。

于 2014-12-05T12:05:19.150 回答
0

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

于 2008-12-08T20:41:55.983 回答
0

这些答案都不适合我,实际上,您不必做任何自定义,您只需在模型类中按照您想要的顺序对字段进行排序。例如...下面的代码

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 )

于 2021-06-09T16:26:35.873 回答
0

要添加一些东西,你可以使用这个(Django 3+):

class ...(forms.ModelForm):
    field = ...

    class Meta:
        model = Xxxxxx
        fields = '__all__'
    
    field_order = ['field', '__all__']

__all__作品

于 2021-11-05T09:10:17.360 回答