[Python] Формы Django, наследование и порядок полей формы



Answers

Из Django 1.9: https://docs.djangoproject.com/en/1.10/ref/forms/api/#notes-on-field-ordering

Оригинальный ответ: Django 1.9 будет поддерживать это по умолчанию в форме с помощью field_order :

class MyForm(forms.Form):
    ...
    field_order = ['field_1', 'field_2']
    ...

https://github.com/django/django/commit/28986da4ca167ae257abcaf7caea230eca2bcd80

Question

Я использую Django-формы на своем веб-сайте и хочу контролировать порядок полей.

Вот как я определяю свои формы:

class edit_form(forms.Form):
    summary = forms.CharField()
    description = forms.CharField(widget=forms.TextArea)


class create_form(edit_form):
    name = forms.CharField()

Имя является неизменным и должно указываться только при создании объекта. Я использую наследование, чтобы добавить последовательность и СУХИЕ принципы. То, что происходит, что не является ошибочным, на самом деле полностью ожидаемым, состоит в том, что поле имени указано последним в представлении / html, но я бы хотел, чтобы поле имени находилось поверх резюме и описания. Я понимаю, что могу легко исправить это, скопировав резюме и описание в create_form и потеряв наследование, но я хотел бы знать, возможно ли это.

Зачем? Представьте, что у вас есть 100 полей в edit_form и вам нужно добавить 10 полей вверху в create_form - копирование и поддержание двух форм не выглядят такими сексуальными. (Это не мой случай, я просто пример)

Итак, как я могу отменить это поведение?

Редактировать:

По-видимому, нет подходящего способа сделать это, не пройдя неприятные хаки (возиться с атрибутом .field). Атрибут .field - это SortedDict (одна из внутренних структур данных Django), которая не позволяет каким-либо образом изменить порядок значений: пары значений. Тем не менее, он обеспечивает способ вставки элементов по заданному индексу, но это перемещает элементы из членов класса и в конструктор. Этот метод будет работать, но сделать код менее читаемым. Единственным другим способом, который я считаю нужным, является изменение самой структуры, которая в большинстве ситуаций является менее оптимальной.

Короче, код будет выглядеть примерно так:

class edit_form(forms.Form):
    summary = forms.CharField()
    description = forms.CharField(widget=forms.TextArea)


class create_form(edit_form):
    def __init__(self,*args,**kwargs):
        forms.Form.__init__(self,*args,**kwargs)

        self.fields.insert(0,'name',forms.CharField())

Это меня закрыло :)




Альтернативные методы изменения порядка поля:

Поп-и-вставки:

self.fields.insert(0, 'name', self.fields.pop('name'))

Поп-и-Append:

self.fields['summary'] = self.fields.pop('summary')
self.fields['description'] = self.fields.pop('description')

Поп-и-добавить-все:

for key in ('name', 'summary', 'description'):
    self.fields[key] = self.fields.pop(key)

Заказанная-копия:

self.fields = SortedDict( [ (key, self.fields[key])
                            for key in ('name', 'summary' ,'description') ] )

Но подход Selene от Django CookBook по-прежнему кажется самым ясным.




Похоже, что в какой-то момент базовая структура порядка поля была изменена с SordedDict специфического SordedDict на стандарт python OrderedDict

Таким образом, в 1.7 мне пришлось сделать следующее:

from collections import OrderedDict

class MyForm(forms.Form):

    def __init__(self, *args, **kwargs):
        super(MyForm, self).__init__(*args, **kwargs)
        original_fields = self.fields
        new_order = OrderedDict()
        for key in ['first', 'second', ... 'last']:
            new_order[key] = original_fields[key]
        self.fields = new_order

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




Я построил форму «ExRegistrationForm», унаследованную от «RegistrationForm» из Django-Registration-Redux. Я столкнулся с двумя проблемами, одна из которых заключалась в переупорядочивании полей на странице вывода html после создания новой формы.

Я решил их следующим образом:

1. ВЫПУСК 1: удалить имя пользователя из регистрационной формы: в my_app.forms.py

    class ExRegistrationForm(RegistrationForm):
          #Below 2 lines extend the RegistrationForm with 2 new fields firstname & lastname
          first_name = forms.CharField(label=(u'First Name'))
          last_name = forms.CharField(label=(u'Last Name'))

          #Below 3 lines removes the username from the fields shown in the output of the this form
          def __init__(self, *args, **kwargs):
          super(ExRegistrationForm, self).__init__(*args, **kwargs)
          self.fields.pop('username')

2. ВЫПУСК 2: Сделать FirstName и LastName сверху: В шаблонах / registration / registration_form.html

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

     {% extends "base.html" %}
     {% load i18n %}

     {% block content %}
     <form method="post" action=".">
          {% csrf_token %}

          #The Default html is: {{ form.as_p }} , which can be broken down into individual elements as below for re-ordering.
          <p>First Name: {{ form.first_name }}</p>
          <p>Last Name:  {{ form.last_name }}</p>
          <p>Email: {{ form.email }}</p>
          <p>Password: {{ form.password1 }}</p>
          <p>Confirm Password: {{ form.password2 }}</p>

          <input type="submit" value="{% trans 'Submit' %}" />
      </form>
      {% endblock %}



Подход принятого ответа использует внутренний API форм Django, который был изменен в Django 1.7. Мнение проектной команды заключается в том, что ее никогда не следует использовать в первую очередь. Теперь я использую эту функцию для изменения порядка моих форм. В этом коде используется OrderedDict :

def reorder_fields(fields, order):
    """Reorder form fields by order, removing items not in order.

    >>> reorder_fields(
    ...     OrderedDict([('a', 1), ('b', 2), ('c', 3)]),
    ...     ['b', 'c', 'a'])
    OrderedDict([('b', 2), ('c', 3), ('a', 1)])
    """
    for key, v in fields.items():
        if key not in order:
            del fields[key]

    return OrderedDict(sorted(fields.items(), key=lambda k: order.index(k[0])))

Что я использую в таких классах:

class ChangeOpenQuestion(ChangeMultipleChoice):

    def __init__(self, *args, **kwargs):
        super(ChangeOpenQuestion, self).__init__(*args, **kwargs)
        key_order = ['title',
                     'question',
                     'answer',
                     'correct_answer',
                     'incorrect_answer']

        self.fields = reorder_fields(self.fields, key_order)



Links