Александр Кошелев
Александр Кошелев Python-разработчик

К порядку!

Довольно часто в форумах, либо в рассылках попадаются вопрос типа: “как изменить порядок вывода полей в форме?”. Решение на самом деле тривиальное, но нигде явно не описано. Надо этот пробел заполнить:-).

Задача изменения порядка вывода полей формы актуальная для форм, которые создаются на базе моделей (позволю себе такое определение) автоматически, т.е. через ModelForm. В таких случаях иной раз нет возможности редактировать саму модель. Порядок играет роль при итерации по полям или при использовании стандартных хелперов рендеринга (as_p, as_table и т.п.).

А откуда же вообще берется очерёдность полей? Очевидно, что она зависит от того, как объявлены поля в определении модели (кстати, для тех форм, где поля декларируются вручную, это тоже справедливо). Это делается через хитрый механизм со статическим счетчиком (creation_counter'ом) и SortedDict внутри джанги. Но это отдельная тема для обсуждения. Вернемся к порядку :-).

Для форм, которые созданы на базе моделей или вручную, порядок полей можно легко изменить. Поможет в этом сам SortedDict. Для примера возьмем форму для редактирования стандартной модели User c некоторым подмножеством полей:

class UserForm(forms.ModelForm):
    class Meta:
        model = User
        fields = ("username", "first_name", "last_name", "email")

Поля класс формы держит в атрибуте base_fields, который и есть SortedDict:

>>> UserForm.base_fields.__class__
<class 'django.utils.datastructures.SortedDict'>
>>> [(name, f) for name, f in UserForm.base_fields.items()] 
[('username', <django.forms.fields.CharField object at 0x877c06c>),
 ('first_name', <django.forms.fields.CharField object at 0x877c82c>),
 ('last_name', <django.forms.fields.CharField object at 0x877f06c>),
 ('email', <django.forms.fields.EmailField object at 0x877f8ac>)]

Если посмотреть реализацию SortedDict, то станет понятно, что “порядок хранится” в списке keyOrder, в котором перечислены ключи в нужной последовательности:

>>> UserForm.base_fields.keyOrder
['username', 'first_name', 'last_name', 'email']

А раз это обычный список, то и делать с ним можно всё что угодно. Например развернуть:

>>> UserForm.base_fields.keyOrder.reverse()
>>>  UserForm.base_fields.keyOrder
['email', 'last_name', 'first_name', 'username']
>>> [(name, f) for name, f in UserForm.base_fields.items()] 
[('email', <django.forms.fields.EmailField object at 0x877f8ac>),
 ('last_name', <django.forms.fields.CharField object at 0x877f06c>),
 ('first_name', <django.forms.fields.CharField object at 0x877c82c>),
 ('username', <django.forms.fields.CharField object at 0x877c06c>)]

Или поставить email сразу после username:

>>> UserForm.base_fields.keyOrder.insert(1, UserForm.base_fields.keyOrder.pop(-1)) 
>>> UserForm.base_fields.keyOrder
['username', 'email', 'first_name', 'last_name']
>>> [(name, f) for name, f in UserForm.base_fields.items()]Out[4]: 
[('username', <django.forms.fields.CharField object at 0x877c06c>),
 ('email', <django.forms.fields.EmailField object at 0x877f88c>),
 ('first_name', <django.forms.fields.CharField object at 0x877c80c>),
 ('last_name', <django.forms.fields.CharField object at 0x877f04c>)]

После таких манипуляций все объекты этой формы будут иметь заданный порядок полей. Кстати, сами объекты формы хранят поля в своём атрибуте fields, над которым тоже можно издеваться, но это уже будет иметь эффект только на данный объект.

Удобно, правда?

comments powered by Disqus