Làm thế nào Django biết thứ tự để hiển thị các trường biểu mẫu?


89

Nếu tôi có biểu mẫu Django chẳng hạn như:

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

Và tôi gọi phương thức as_table () của một thể hiện của biểu mẫu này, Django sẽ hiển thị các trường theo thứ tự như đã chỉ định ở trên.

Câu hỏi của tôi là làm thế nào Django biết thứ tự mà các biến lớp được xác định ở đâu?

(Ngoài ra, làm cách nào để ghi đè thứ tự này, chẳng hạn như khi tôi muốn thêm một trường từ phương thức init của classe ?)

Câu trả lời:


89

[LƯU Ý: câu trả lời này hiện đã khá lỗi thời - vui lòng xem thảo luận bên dưới nó và các câu trả lời mới hơn].

Nếu flà một biểu mẫu, các trường của nó f.fieldslà a django.utils.datastructures.SortedDict (nó trình bày các mục theo thứ tự chúng được thêm vào). Sau khi xây dựng biểu mẫu f.fields có thuộc tính keyOrder, là danh sách chứa các tên trường theo thứ tự chúng sẽ được trình bày. Bạn có thể đặt điều này theo thứ tự chính xác (mặc dù bạn cần phải cẩn thận để đảm bảo bạn không bỏ sót các mục hoặc thêm các phần bổ sung).

Đây là một ví dụ tôi vừa tạo trong dự án hiện tại của mình:

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
Hiện tại, các trường sẽ được sắp xếp theo thứ tự do thuộc tính 'fields' của ModelForm chỉ định. Từ docs.djangoproject.com/en/1.3/topics/forms/modelforms/… : Thứ tự mà tên trường được chỉ định trong danh sách đó được tuân thủ khi biểu mẫu hiển thị chúng. Điều này chỉ hoạt động với ModelForms - cách tiếp cận của bạn vẫn khá hữu ích cho các biểu mẫu thông thường, đặc biệt là trong các lớp con của biểu mẫu có thêm các trường bổ sung.
Chris Lawlor

3
Điều này hoạt động khi bạn đang làm những thứ thú vị ghi đè một số trường nhưng không phải những trường khác. +1
Nathan Keller

"Đây" là gợi ý của Chris Lawlor? Câu trả lời này đủ cũ để nó có thể không còn hiện tại nữa (nhưng có lẽ tốt cho 1,2). Điều quan trọng là đảm bảo rằng thông tin không đáng tin cậy sẽ bị gắn cờ, nếu không những người mới đến sẽ không biết nên tin tưởng vào điều gì. Cảm ơn vì đầu vào của bạn.
holdenweb

67

Mới đối với Django 1.9 là 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
Đây là câu trả lời tôi đang tìm kiếm!
sivabudh

1
kể từ ngày 1.9, đây sẽ là câu trả lời mà những người tìm kiếm mới nên xem xét, rõ ràng là vẫn hoạt động trong 1.10
Brian H.

2
Lưu ý: đó không phải là "fields_order" mà là "field_order" (no 's')
Davy

1
Bạn thậm chí có thể chỉ định chỉ một tập hợp con của các trường biểu mẫu bạn muốn sắp xếp
danleyb2

40

Tôi đã đi trước và trả lời câu hỏi của riêng tôi. Đây là câu trả lời để tham khảo trong tương lai:

Trong Django form.pythực hiện một số phép thuật đen tối bằng cách sử dụng __new__phương thức để tải các biến lớp của bạn cuối cùng vào self.fieldstheo thứ tự được xác định trong lớp. self.fieldslà một SortedDictcá thể Django (được định nghĩa trong datastructures.py).

Vì vậy, để ghi đè điều này, hãy nói trong ví dụ của tôi, bạn muốn người gửi đến trước nhưng cần thêm nó vào một phương thức init , bạn sẽ làm như sau:

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
Bạn có thể xác định các lĩnh vực 'gửi' như bình thường ở phía trên và sau đó sử dụng một biến thể của dòng chèn của bạn:self.fields.insert( 0, 'sender', self.fields['sender'] )
EvdB

4
Điều này không còn hoạt động trong Django 1.7 vì các trường biểu mẫu sử dụng OrderedDict không hỗ trợ append. Xem câu trả lời cập nhật của tôi bên dưới (quá dài để nhận xét) ...
Paul Kenjora

11

Các trường được liệt kê theo thứ tự chúng được xác định trong ModelClass._meta.fields. Nhưng nếu bạn muốn thay đổi thứ tự trong Biểu mẫu, bạn có thể thực hiện bằng cách sử dụng hàm keyOrder. Ví dụ :

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

Với Django> = 1.7, bạn phải sửa đổi ContactForm.base_fieldsnhư sau:

from collections import OrderedDict

...

class ContactForm(forms.Form):
    ...

ContactForm.base_fields = OrderedDict(
    (k, ContactForm.base_fields[k])
    for k in ['your', 'field', 'in', 'order']
)

Thủ thuật này được sử dụng trong Django Admin PasswordChangeForm: Nguồn trên Github


3
Thật buồn cười, tôi đã tìm kiếm thứ tự của các trường khi cố gắng tìm ra lý do tại sao phân lớp PasswordChangeFormthay đổi thứ tự của các trường, vì vậy ví dụ của bạn tình cờ rất hữu ích đối với tôi. Có vẻ như đó là bởi vì base_fieldsnó không được chuyển sang lớp con. Các giải pháp dường như được nói PasswordChangeFormSubclass.base_fields = PasswordChangeForm.base_fieldssau khi định nghĩa của `PasswordChangeFormSubclass
orblivion

@orblivion: sử dụng ContactForm.base_fields[k]khi xây dựng các dict có thứ tự. Có một lỗi đánh máy cho câu trả lời, tôi sẽ chỉnh sửa
blueFast

5

Các trường biểu mẫu có một thuộc tính cho thứ tự tạo, được gọi creation_counter. .fieldsthuộc tính là một từ điển, vì vậy việc thêm đơn giản vào từ điển và thay đổi creation_countercác thuộc tính trong tất cả các trường để phản ánh thứ tự mới là đủ (tuy nhiên, chưa bao giờ thử điều này).


5

Sử dụng bộ đếm trong lớp Trường. Sắp xếp theo quầy đó:

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

Đầu ra:

[Field('b'), Field('a'), Field('c')]


4

Đối với Django 1.7, các biểu mẫu sử dụng OrderedDict không hỗ trợ toán tử nối thêm. Vì vậy, bạn phải xây dựng lại từ điển từ đầu ...

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 

Kể từ python 3.2, bạn có thể sử dụng OrderedDict.move_to_end () để thêm trước hoặc nối thêm các mục.
bparker

3

Để tham khảo trong tương lai: mọi thứ đã thay đổi một chút kể từ khi có dạng mới. Đây là một cách sắp xếp lại thứ tự các trường từ các lớp biểu mẫu cơ sở mà bạn không có quyền kiểm soát:

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

Ngoài ra, có một chút tài liệu về SortedDict base_fieldssử dụng tại đây: http://code.djangoproject.com/wiki/SortedDict


Sử dụng 'trường' trong lớp của tôi Meta đã hoạt động đối với tôi (với ModelForm) cho đến khi tôi có lý do để đặt giá trị ban đầu cho trường khóa ngoại bằng MyFormSet.form.base_fields, tại thời điểm đó, thứ tự trong 'trường' là không còn được tôn trọng. Giải pháp của tôi thực ra chỉ là sắp xếp lại các trường trong mô hình cơ bản.
Paul J

2

Nếu một trong hai fields = '__all__':

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

hoặc excludeđược sử dụng:

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

Sau đó, Django tham chiếu thứ tự của các trường như được xác định trong mô hình . Điều này khiến tôi không khỏi ngạc nhiên, vì vậy tôi nghĩ mình sẽ đề cập đến nó. Nó được tham chiếu trong tài liệu ModelForm :

Nếu một trong hai điều này được sử dụng, thứ tự các trường xuất hiện trong biểu mẫu sẽ là thứ tự các trường được xác định trong mô hình, với các trường hợp ManyToManyField xuất hiện sau cùng.


2

Cách dễ nhất để sắp xếp các trường trong biểu mẫu django 1.9 là sử dụng field_ordertrong biểu mẫu Form.field_order của bạn

Đây là một ví dụ nhỏ

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

Điều này sẽ hiển thị mọi thứ theo thứ tự bạn đã chỉ định trong field_orderdict.


1

Sử dụng fieldstrong Metalớp bên trong là những gì hiệu quả với tôi 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']

Đơn giản vậy thôi.


1
có vẻ như phương pháp này chỉ có giá trị cho ModelForm, hình thức bình thường django.formskhông hoạt động
Pengfei.X

1

Tôi đã sử dụng điều này để di chuyển các trường về:

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)

Điều này hoạt động trong 1.5 và tôi chắc chắn rằng nó vẫn hoạt động trong các phiên bản gần đây hơn.


0

Nó liên quan đến lớp meta được sử dụng để xác định lớp biểu mẫu. Tôi nghĩ rằng nó giữ một danh sách nội bộ của các trường và nếu bạn chèn vào giữa danh sách, nó có thể hoạt động. Đã một thời gian kể từ khi tôi nhìn vào mã đó.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.