Djangoはフォームフィールドをレンダリングする順序をどのように知っていますか?


89

私が次のようなDjangoフォームを持っている場合:

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

そして、このフォームのインスタンスのas_table()メソッドを呼び出すと、Djangoはフィールドを上記で指定されたのと同じ順序でレンダリングします。

私の質問は、クラス変数が定義されている順序をDjangoがどのように知るかです。

(たとえば、クラスのinitメソッドからフィールドを追加したい場合など、この順序をどのようにオーバーライドしますか?)

回答:


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

19
しばらくの間、フィールドはModelFormの 'fields'属性で指定された順序で並べられます。docs.djangoproject.com/en/1.3/topics/forms/modelforms/…から :フォームがそれらをレンダリングするとき、そのリストで指定されたフィールド名の順序が尊重されます。これはModelFormsでのみ機能します。この方法は、通常のフォーム、特に追加のフィールドを追加するフォームサブクラスでは依然として非常に役立ちます。
Chris Lawlor、2011年

3
これは、一部のフィールドをオーバーライドして他のフィールドをオーバーライドしないファンキーなことをしているときに機能します。+1
Nathan Keller、

「これ」はクリス・ローラーの提案ですか?この答えは古すぎておそらく最新ではない(おそらく1.2では良かった)。信頼できない情報にフラグが立てられることを確認することが重要です。ご入力いただきありがとうございます。
holdenweb 2012

67

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

1
これが私が探している答えです!
sivabudh

1
1.9の時点で、これは新しい検索者が見るべき答えとなるはずです。明らかに1.10でも動作します
ブライアンH.

2
注:「fields_order」ではなく「field_order」( 's'なし)
Davy

1
注文したいフォームフィールドのサブセットのみを指定することもできます
danleyb2

40

私は先に進み、自分の質問に答えました。これが将来の参考のための答えです。

Django form.pyでは、__new__メソッドを使用してクラス変数を最終的にself.fieldsクラスで定義された順序でロードするためのダークマジックを実行します。 self.fieldsDjango 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())))

12
あなたは一番上に通常通り「送信者フィールドを定義してから挿入行にバリエーションを使用することができますself.fields.insert( 0, 'sender', self.fields['sender'] )
EvdB

4
フォームフィールドは追加をサポートしないOrderedDictを使用するため、これはDjango 1.7では機能しなくなりました。以下の私の更新された回答を参照してください(コメントするには長すぎます)...
Paul Kenjora

11

フィールドは、ModelClass._meta.fieldsで定義されている順序でリストされます。ただし、フォームの順序を変更する場合は、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']

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上のソースで使用されています


3
おかしい、サブクラス化PasswordChangeFormがフィールドの順序を変更した理由を理解しようとするときにフィールドの順序を探したので、あなたの例はたまたま私にとって非常に役に立ちました。それbase_fieldsはサブクラスに引き継がれないためだと思われます。解決策はPasswordChangeFormSubclass.base_fields = PasswordChangeForm.base_fields、 `PasswordChangeFormSubclass
orblivion

@orblivion:ContactForm.base_fields[k]順序付けられた辞書を構築するときに使用します。答えにタイプミスがあります。編集します
blueFast

5

フォームフィールドには、と呼ばれる作成順序の属性がありますcreation_counter.fieldsattributeはディクショナリーであるため、ディクショナリーに単純に追加しcreation_counter、すべてのフィールドの属性を変更して新しい順序を反映するだけで十分です(ただし、これを試したことはありません)。


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


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 

Python 3.2以降では、OrderedDict.move_to_end()を使用してアイテムを追加または追加できます。
bparker

3

将来の参考のために:物事はnewforms以来少し変わっています。これは、コントロールできない基本フォームクラスからフィールドを並べ替える1つの方法です。

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


MyFormSet.form.base_fieldsを使用して外部キーフィールドの初期値を設定する理由が出るまで、私のクラスMetaで 'fields'を使用すると(ModelFormで)機能していました。より尊敬されています。私の解決策は、実際には、基になるモデルのフィールドを並べ替えることだけでした。
Paul J

2

いずれかfields = '__all__'

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

またはexclude使用されます:

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

次に、Django はモデルで定義されているフィールドの順序を参照します。これで気がついたので、触れておきます。これはModelFormドキュメントで参照されています

これらのいずれかが使用される場合、フィールドがフォームに表示される順序は、フィールドがモデルで定義された順序になり、ManyToManyFieldインスタンスが最後に表示されます。


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で指定した順序ですべてを表示します。


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

それと同じくらい簡単です。


1
このメソッドはmodelformに対してのみ有効であるようですが、通常のフォームdjango.formsは機能しません
Pengfei.X '25

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で動作しますが、最近のバージョンでも動作することは間違いありません。


0

これは、フォームクラスの定義に使用されるメタクラスに関係しています。私はそれがフィールドの内部リストを保持していると思います、そしてあなたがリストの真ん中に挿入すればそれはうまくいくかもしれません。そのコードを見てから久しぶりです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.