Как же Django знать для того, чтобы сделать поля форм?


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

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

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

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

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

14 81

14 ответов:

[Примечание: этот ответ теперь довольно полностью устарел - см. обсуждение ниже и более поздние ответы].

Если F представляет собой форму, ее поля Ф.поля, что является 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

New to Django 1.9 is форма.field_order и форма.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']

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

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

поля перечислены в том порядке, в котором они определены в классе ModelClass._мета.поля. Но если вы хотите изменить порядок в форме, вы можете сделать это с помощью функции 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']

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

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

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

С 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

в формах Django 1.7 используется OrderedDict, который не поддерживает оператор append. Так что вам придется перестраивать словарь с нуля...

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 

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

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 = '__all__':

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

или exclude используется:

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

тогда Django ссылается на порядок полей как определено в модели. Это просто застало меня врасплох, так что я решил упомянуть об этом. На него ссылаются в ModelForm docs:

если используется любой из них, порядок отображения полей в форме будет соответствовать порядку, определенному в модели, с появлением экземпляров ManyToManyField последний.

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

используя 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']

вот так просто.

я использовал это для перемещения полей:

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, и я уверен, что он по-прежнему работает в более поздних версиях.

самый простой способ заказать поля в формах django 1.9-использовать field_order в форме форма.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 дикт.