Как Django знает порядок отображения полей формы? - PullRequest
84 голосов
/ 08 декабря 2008

Если у меня есть форма Django, такая как:

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

И я вызываю метод as_table () экземпляра этой формы, Django отобразит поля в том же порядке, как указано выше.

Мой вопрос: как Django узнает, в каком порядке определены переменные класса?

(Также как мне переопределить этот порядок, например, когда я хочу добавить поле из метода init класса?)

Ответы [ 14 ]

89 голосов
/ 28 июля 2009

[ПРИМЕЧАНИЕ: этот ответ в настоящее время полностью устарел - см. Обсуждение ниже и более свежие ответы].

Если 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
45 голосов
/ 29 декабря 2015

Новое в 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')

    fields_order = ['username', 'custom_field', 'password']
38 голосов
/ 09 декабря 2008

Я пошел дальше и ответил на свой вопрос. Вот ответ для дальнейшего использования:

В Django form.py совершает некоторую темную магию, используя метод __new__ для загрузки переменных вашего класса в конечном итоге в self.fields в порядке, определенном в классе. self.fields - это экземпляр Django 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())))
11 голосов
/ 21 апреля 2011

Поля перечислены в порядке их определения в 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 голосов
/ 22 декабря 2014

С 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: Источник на Github

5 голосов
/ 16 мая 2009

Используйте счетчик в классе поля. Сортировать по этому счетчику:

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

5 голосов
/ 08 декабря 2008

Поля формы имеют атрибут для порядка создания, который называется creation_counter. .fields атрибут - это словарь, поэтому простого добавления в словарь и изменения creation_counter атрибутов во всех полях, чтобы отразить новое упорядочение, должно быть достаточно (хотя никогда не пытался это сделать).

4 голосов
/ 04 марта 2015

Начиная с 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 голосов
/ 31 марта 2010

Для дальнейшего использования: все изменилось с момента появления новых форм. Это один из способов переупорядочения полей из базовых классов форм, которые вы не можете контролировать:

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

2 голосов
/ 09 сентября 2016

Самый простой способ упорядочить поля в формах 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_order dict.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...