70

Web サイトで Django フォームを使用しており、フィールドの順序を制御したいと考えています。

フォームを定義する方法は次のとおりです。

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


class create_form(edit_form):
    name = forms.CharField()

名前は不変であり、エンティティの作成時にのみリストする必要があります。継承を使用して、一貫性と DRY 原則を追加します。間違いではなく、実際には完全に予想されるのは、名前フィールドがview/htmlの最後にリストされていることですが、名前フィールドを概要と説明の上に配置したいと思います。要約と説明を create_form にコピーして継承を失うことで簡単に修正できることはわかっていますが、これが可能かどうか知りたいです。

なんで?edit_form に 100 個のフィールドがあり、create_form の一番上に 10 個のフィールドを追加する必要があると想像してください。2 つのフォームをコピーして維持するのはそれほど魅力的ではありません。(これは私の場合ではありません。例を作成しているだけです)

では、どうすればこの動作をオーバーライドできますか?

編集:

どうやら厄介なハック(.field属性をいじる)を経ずにこれを行う適切な方法はないようです。.field 属性は、キーと値のペアを並べ替える方法を提供しない SortedDict (Django の内部データ構造の 1 つ) です。ただし、特定のインデックスに項目を挿入する方法は提供されますが、それでは項目がクラス メンバーからコンストラクターに移動されます。この方法は機能しますが、コードが読みにくくなります。私が適切と考える他の唯一の方法は、ほとんどの状況で最適とは言えないフレームワーク自体を変更することです。

要するに、コードは次のようになります。

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

それは私を黙らせました:)

4

11 に答える 11

107

Django 1.9 以降から

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 1.6 まで

これと同じ問題があり、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']
于 2009-07-15T19:29:22.687 に答える
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

于 2015-06-12T03:27:41.527 に答える
12

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'])
于 2010-04-12T02:38:58.320 に答える
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()
于 2011-12-12T15:26:51.133 に答える
6

受け入れられた回答のアプローチは、Django 1.7 で変更された内部 Django フォーム API を利用します。プロジェクトチームの意見は、そもそも使用されるべきではなかったというものです。この関数を使用して、フォームを並べ替えます。このコードは以下を利用します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 ChangeOpenQuestionForm(ChangeMultipleChoiceForm):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        key_order = ['title',
                     'question',
                     'answer',
                     'correct_answer',
                     'incorrect_answer']

        self.fields = reorder_fields(self.fields, key_order)
于 2014-12-15T21:53:46.140 に答える
3

Djangoの内部がフィールドの順序を追跡する方法については、このSOの質問のメモを参照してください。答えには、フィールドを好みに合わせて「並べ替える」方法に関する提案が含まれています(最終的には、属性をいじることになり.fieldsます)。

于 2009-05-27T02:05:53.197 に答える
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') ] )

しかし、Django CookBook からの Selene のアプローチは、依然として最も明確に感じられます。

于 2009-11-09T21:22:10.813 に答える
1

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

上記では、 django-easycontactusパッケージで定義されている EasyContactUsForm 基本クラスを拡張しています。

于 2012-09-17T16:40:49.033 に答える
0

Django-Registration-Redux の「RegistrationForm」から継承したフォーム「ExRegistrationForm」を作成しました。私は 2 つの問題に直面しました。そのうちの 1 つは、新しいフォームが作成された後に、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 %}
于 2015-10-09T09:31:11.177 に答える