Django는 양식 필드를 렌더링하는 순서를 어떻게 알고 있습니까?


91

다음과 같은 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.fieldsA는, 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

20
당분간 필드는 ModelForm의 'fields'속성에 지정된대로 정렬됩니다. From docs.djangoproject.com/en/1.3/topics/forms/modelforms/… : 해당 목록에서 필드 이름이 지정되는 순서는 양식이 렌더링 할 때 준수됩니다. 이것은 ModelForms에서만 작동합니다. 사용자의 접근 방식은 일반 양식, 특히 추가 필드를 추가하는 양식 하위 클래스에서 여전히 매우 유용합니다.
Chris Lawlor

3
이것은 일부 필드를 재정의하고 다른 필드를 재정의하는 펑키 한 작업을 할 때 작동합니다. +1
Nathan Keller

"이것은"Chris Lawlor의 제안입니까? 이 답변은 아마도 더 이상 최신이 아닐 정도로 충분히 오래되었습니다 (하지만 아마도 1.2에서는 좋았을 것입니다). 신뢰할 수없는 정보에 플래그가 지정되도록하는 것이 중요합니다. 그렇지 않으면 신규 이민자가 무엇을 신뢰할 수 있는지 알 수 없습니다. 귀하의 의견에 감사드립니다.
holdenweb

72

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
'sender'필드를 상단에서 정상적으로 정의한 다음 삽입 라인에 변형을 사용할 수 있습니다.self.fields.insert( 0, 'sender', self.fields['sender'] )
EvdB

4
양식 필드는 추가를 지원하지 않는 OrderedDict를 사용하기 때문에 Django 1.7에서 더 이상 작동하지 않습니다. (너무 오래 코멘트) 아래에있는 내 업데이트 된 답변을 참조하십시오
폴 Kenjora

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

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 PasswordChangeForm: Source on Github 에서 사용됩니다 .


3
재미있게도, 나는 왜 서브 클래 싱 PasswordChangeForm이 필드의 순서를 바꾸 었는지 알아 내려고 할 때 필드의 순서를 찾았습니다 . 그래서 당신의 예제는 나에게 매우 유용했습니다. base_fields하위 클래스로 넘어 가지 않기 때문인 것 같습니다 . 이 솔루션은 말하는 것 같다 PasswordChangeFormSubclass.base_fields = PasswordChangeForm.base_fieldsPasswordChangeFormSubclass '의 정의 후
orblivion

@orblivion : ContactForm.base_fields[k]정렬 된 dict를 빌드 할 때 사용 합니다. 오타 N 답이있다, 나는 편집 할 수 있습니다
blueFast

5

양식 필드에는라는 생성 순서에 대한 속성이 creation_counter있습니다. .fields속성은 사전이므로 사전에 간단하게 추가하고 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 

파이썬 3.2부터 OrderedDict.move_to_end ()를 사용하여 항목을 추가하거나 추가 할 수 있습니다.
bparker

3

향후 참조 : newform 이후 상황이 약간 변경되었습니다. 이것은 제어 할 수없는 기본 폼 클래스에서 필드를 재정렬하는 한 가지 방법입니다.

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 클래스에서 '필드'를 사용하는 것이 저에게 효과적이었습니다. 더 오래 존경받습니다. 내 솔루션은 실제로 기본 모델의 필드를 다시 정렬하는 것입니다.
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 양식에서 필드를 정렬하는 가장 쉬운 방법은 Form.field_orderfield_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

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.