Как Django знает порядок для визуализации полей формы?
Если у меня есть форма Django, такая как:
class ContactForm(forms.Form):
subject = forms.CharField(max_length=100)
message = forms.CharField()
sender = forms.EmailField()
И я вызываю метод as_table() экземпляра этой формы, Django будет отображать поля в том же порядке, как указано выше.
Мой вопрос в том, как Django знает порядок, в котором определены переменные класса?
(Также как переопределить этот порядок, например, когда я хочу добавить поле из метода init)
Ответы
Ответ 1
Я пошел вперед и ответил на свой вопрос. Вот ответ для дальнейшего использования:
В 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())))
Ответ 2
[ПРИМЕЧАНИЕ: этот ответ в настоящее время полностью устарел - смотрите обсуждение ниже и более свежие ответы].
Если 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
Ответ 3
Новым в 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']
Ответ 4
Поля указаны в том порядке, в котором они определены в 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']
Ответ 5
С 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
Ответ 6
Поля формы имеют атрибут для порядка создания, называемый creation_counter
. Атрибут .fields
- это словарь, поэтому простое добавление словаря и изменение атрибутов creation_counter
во всех полях, чтобы отразить новый порядок, должно быть достаточным (хотя и не пытались это сделать).
Ответ 7
Используйте счетчик в классе 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')]
Ответ 8
В формах 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
Ответ 9
Для справок в будущем: все изменилось с тех пор, как новые формы. Это один из способов переупорядочения полей из базовых классов классов, которые вы не контролируете:
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
Ответ 10
Если либо fields = '__all__'
:
class AuthorForm(ModelForm):
class Meta:
model = Author
fields = '__all__'
или exclude
:
class PartialAuthorForm(ModelForm):
class Meta:
model = Author
exclude = ['title']
Затем Django ссылается на порядок полей , как определено в модели. Это меня просто укусило, поэтому я подумал, что упомянул об этом. Он ссылается на Документы ModelForm:
Если любое из них используется, порядок, в котором поля появятся в форме, будет порядком, в котором будут определены поля в модели, а экземпляры ManyToManyField появятся последними.
Ответ 11
Самый простой способ упорядочить поля в формах 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.
Ответ 12
Использование 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']
Проще всего.
Ответ 13
Это связано с мета-классом, который используется при определении класса формы. Я думаю, что он хранит внутренний список полей, и если вы вставляете его в середину списка, это может сработать. Прошло некоторое время с тех пор, как я просмотрел этот код.
Ответ 14
Я использовал это для перемещения полей:
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, и я уверен, что он по-прежнему работает в более поздних версиях.