К порядку!

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

Задача изменения порядка вывода полей формы актуальная для форм, которые создаются на базе моделей (позволю себе такое определение) автоматически, т.е. через 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, над которым тоже можно издеваться, но это уже будет иметь эффект только на данный объект.

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

Комментарии 12

  1. scey написал:

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

    PS: У вас в fields класса UserForm скобки разные стоят.

    PPS: Если комментарий не сразу добавлять, а делать "просмотр", при его сохранении получается "страница не найдена".

    Оставлен 07 Январь 2009 в 21:18
  2. Анатолий написал:

    Привет, Александр.

    Вопрос не совсем в тему поста, но рядом.

    Как наиболее правильно автоматически создать форму из N моделей, при этом изменить порядок полей в форме?

    Оставлен 08 Январь 2009 в 09:35
  3. мимо проходил... написал:

    а как в таких формах с валидацией, в каком порядке она будет проходить для сортированных таким образом полей?

    P.S. если форма комментария не прошла валидацию - капча не отображается

    Оставлен 08 Январь 2009 в 14:56
  4. Александр Кошелев написал:

    За багрепорты спасибо. При следующем апдейте учту.

    Оставлен 08 Январь 2009 в 19:26
  5. Александр Кошелев написал:

    Как наиболее правильно автоматически создать форму из N моделей, при этом изменить порядок полей в форме?

    Я бы попробовал через fields_for_model собрать все поля со всех моделей и потом на базе них сделать форму. И в ней уже тасовал бы поля.

    Оставлен 08 Январь 2009 в 19:31
  6. Александр Кошелев написал:

    а как в таких формах с валидацией, в каком порядке она будет проходить для сортированных таким образом полей?

    В заданном порядке. Т.е. все операции с полями будут происходить в новом указанном порядке.

    Оставлен 08 Январь 2009 в 19:32
  7. Сергей Шепелев написал:

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

    И здесь явно не описано. ;) надо заполнить.

    class MyForm(forms.ModelForm):
        def __init__(self, *args, **kwargs):
            super(forms.ModelForm, self).__init__(*args, **kwargs)
            self.fields.keyOrder = MyForm.Meta.fields
    
        class Meta:
            model = MyModel
            fields = ('name', 'email', 'password', 'task')
    

    в данном случае, порядок берется из списка полей. Можно завести отдельный список.

    Оставлен 11 Январь 2009 в 03:44
  8. bw написал:

    Очень символично, что теперь ты даже не указываешь инструмент, о котором повествуешь :-).

    p.s. Я и сам за собой такое замечал, в контексте Linux.

    ..bw

    Оставлен 13 Январь 2009 в 17:30
  9. Александр Кошелев написал:

    Ну ладно тебе, тег то у поста тематический выставлен:-) На него и надо ориентироваться:-)

    Оставлен 13 Январь 2009 в 19:24
  10. bw написал:

    Да я так, просто Django не люблю ;-). Да и просто, стоит иногда опускаться до мерских проблем... (Не все используют Django, не все работают в Linux.)

    p.s. Получил через "контакты" сообщение про комментарии?

    ..bw

    Оставлен 14 Январь 2009 в 21:00
  11. Александр Кошелев написал:

    Не, джангу надо любить:-)

    p.s. Получил через "контакты" сообщение про комментарии?

    Да, спасибо. В очередной раз помог;-)

    Оставлен 15 Январь 2009 в 00:48
  12. Fox написал:

    Да Саша описал проблему но не дал решения.

    Сергей Шепелев вроде дал единственное исправление

    class MyForm(forms.ModelForm):

    def __init__(self, *args, **kwargs):
        super(forms.ModelForm, self).__init__(*args, **kwargs)
        #было
        self.fields.keyOrder = MyForm.Meta.fields
        #стало
        self.fields.keyOrder = self.Meta.fields
    
    class Meta:
        model = MyModel
        fields = ('name', 'email', 'password', 'task')
    

    Наверное это даже можно вынести куда нибудь в одно место. и упорядоченность будет подчинятся fields.

    Оставлен 19 Январь 2009 в 18:42

Пингбеки 2

  1. От Изменить порядок полей в динамически формируемой форме 14 Сентябрь 2010 в 07:59

    webnewage.org/2009/01/08/k-poryadku/Но для динамической форме это не помагает. Как можно привести форму к порядку?

  2. От Наследник формы с другим порядком полей 18 Апрель 2010 в 09:24

    Пост на эту тему — http://webnewage.org/2009/01/08/k-poryadku/