Django รู้คำสั่งในการสร้างฟิลด์แบบฟอร์มได้อย่างไร


92

หากฉันมีรูปแบบ Django เช่น:

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

และฉันเรียกวิธี as_table () ของอินสแตนซ์ของแบบฟอร์มนี้ Django จะแสดงผลฟิลด์ตามลำดับเดียวกันกับที่ระบุไว้ข้างต้น

คำถามของฉันคือ Django รู้ลำดับที่ตัวแปรคลาสที่กำหนดไว้ได้อย่างไร

(ฉันจะแทนที่คำสั่งนี้ได้อย่างไรเช่นเมื่อฉันต้องการเพิ่มฟิลด์จากวิธีการเริ่มต้นของ classe )

คำตอบ:


89

[หมายเหตุ: คำตอบนี้ค่อนข้างล้าสมัยไปแล้ว - โปรดดูการสนทนาด้านล่างและคำตอบล่าสุด]

ถ้าfเป็นรูปแบบช่องของมันf.fieldsคือ a 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
ในบางครั้งระบบจะเรียงลำดับฟิลด์ตามที่ระบุโดยแอตทริบิวต์ "fields" ของ ModelForm จาก docs.djangoproject.com/en/1.3/topics/forms/modelforms/… : ลำดับที่ระบุชื่อฟิลด์ในรายการนั้นจะได้รับการเคารพเมื่อฟอร์มแสดงผล วิธีนี้ใช้ได้กับ ModelForms เท่านั้น - แนวทางของคุณยังค่อนข้างมีประโยชน์สำหรับรูปแบบปกติโดยเฉพาะในคลาสย่อยของฟอร์มที่เพิ่มฟิลด์เพิ่มเติม
Chris Lawlor

3
วิธีนี้ใช้ได้ผลเมื่อคุณทำสิ่งที่ขี้ขลาดเหนือเขตข้อมูลบางช่อง แต่ไม่ใช่ช่องอื่น ๆ +1
Nathan Keller

"นี่" เป็นคำแนะนำของ Chris Lawlor หรือไม่? คำตอบนี้เก่าพอที่อาจจะไม่เป็นปัจจุบันอีกต่อไป (แต่น่าจะดีสำหรับ 1.2) สิ่งสำคัญคือต้องตรวจสอบให้แน่ใจว่าข้อมูลที่ไม่น่าเชื่อถือถูกตั้งค่าสถานะมิฉะนั้นผู้มาใหม่จะไม่รู้ว่าควรเชื่อถืออะไร ขอบคุณสำหรับข้อมูลของคุณ
holdenweb

73

ใหม่เพื่อ Django 1.9 เป็นForm.field_orderและForm.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
Brian H.

2
หมายเหตุ: ไม่ใช่ "fields_order" แต่เป็น "field_order" (no 's')
Davy

1
คุณสามารถระบุเพียงส่วนย่อยของฟิลด์แบบฟอร์มที่คุณต้องการสั่งซื้อ
danleyb2

40

ฉันตอบคำถามของตัวเองต่อไป นี่คือคำตอบสำหรับการอ้างอิงในอนาคต:

ใน Django form.pyใช้เวทมนตร์แห่งความมืดโดยใช้__new__วิธีการโหลดตัวแปรคลาสของคุณในท้ายที่สุดself.fieldsตามลำดับที่กำหนดไว้ในคลาส self.fieldsเป็นSortedDictอินสแตนซ์Django (กำหนดไว้ในdatastructures.py)

เพื่อลบล้างสิ่งนี้ในตัวอย่างของฉันคุณต้องการให้ผู้ส่งมาก่อน แต่จำเป็นต้องเพิ่มในวิธีการเริ่มต้นคุณจะต้องทำ:

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
สิ่งนี้ใช้ไม่ได้ใน Django 1.7 อีกต่อไปเนื่องจากฟิลด์แบบฟอร์มใช้ OrderDict ซึ่งไม่รองรับการผนวก ดูคำตอบที่อัปเดตของฉันด้านล่าง (ยาวเกินไปสำหรับความคิดเห็น) ...
Paul 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_fieldsตามคำจำกัดความของ `` PasswordChangeFormSubclass
orblivion

@orblivion: ใช้ContactForm.base_fields[k]เมื่อสร้างคำสั่งที่สั่ง มีการพิมพ์ผิดและคำตอบฉันจะแก้ไข
blueFast

5

creation_counterเขตข้อมูลฟอร์มมีแอตทริบิวต์สำหรับการสั่งซื้อการสร้างที่เรียกว่า .fieldsแอตทริบิวต์เป็นพจนานุกรมดังนั้นการเพิ่มพจนานุกรมและการเปลี่ยนcreation_counterแอตทริบิวต์ในทุกช่องอย่างง่าย ๆ เพื่อให้สอดคล้องกับการจัดลำดับใหม่ก็ควรจะเพียงพอ (ไม่เคยลองทำเช่นนี้)


5

ใช้ตัวนับในคลาสฟิลด์ เรียงตามตัวนับนั้น:

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 ใช้ OrderDict ซึ่งไม่รองรับตัวดำเนินการผนวก ดังนั้นคุณต้องสร้างพจนานุกรมใหม่ตั้งแต่ต้น ...

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 คุณสามารถใช้ OrderDict.move_to_end () เพื่อเพิ่มหรือต่อท้ายรายการ
bparker

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

นอกจากนี้ยังมีเอกสารเล็กน้อยเกี่ยวกับ SortedDict ที่base_fieldsใช้ที่นี่: http://code.djangoproject.com/wiki/SortedDict


การใช้ 'fields' ในคลาสของฉัน Meta ใช้งานได้สำหรับฉัน (ด้วย ModelForm) จนกว่าฉันจะมีเหตุผลที่จะตั้งค่าเริ่มต้นสำหรับฟิลด์คีย์ต่างประเทศโดยใช้ MyFormSet.form.base_fields เมื่อถึงจุดที่ลำดับใน 'ฟิลด์' ไม่ใช่ เคารพอีกต่อไป วิธีแก้ปัญหาของฉันเป็นเพียงการเรียงลำดับฟิลด์ใหม่ในโมเดลต้นแบบ
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

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.