Формы Django, наследование и порядок полей формы
Я использую формы Django на своем веб-сайте и хотел бы контролировать порядок полей.
вот как я определяю свои формы:
class edit_form(forms.Form):
summary = forms.CharField()
description = forms.CharField(widget=forms.TextArea)
class create_form(edit_form):
name = forms.CharField()
имя является неизменяемым и должно быть указано только при создании объекта. Я использую наследование, чтобы добавить последовательность и сухие принципы. Что происходит, что не является ошибочным, на самом деле полностью ожидаемым, так это то, что поле name указано последним в представлении/html, но я бы хотел, чтобы поле name было поверх резюме и описания. Я делаю поймите, что я мог бы легко исправить это, скопировав сводку и описание в create_form и потерять наследование, но я хотел бы знать, возможно ли это.
почему? представьте, что у вас есть 100 полей в edit_form и нужно добавить 10 полей сверху в create_form - копирование и поддержание двух форм не будет выглядеть так сексуально. (Это не мой случай, я просто придумываю пример)
Итак, как я могу переопределить это поведение?
Edit:
по-видимому, нет правильного способа сделать это, не проходя через неприятные хаки (возиться .атрибут поля). Этот.атрибут 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())
что заткни меня:)
10 ответов:
От Django 1.9+
Django 1.9 добавляет новый ,
field_order, что позволяет упорядочить поля независимо от их порядка объявления в классе.class MyForm(forms.Form): summary = forms.CharField() description = forms.CharField(widget=forms.TextArea) author = forms.CharField() notes = form.CharField() field_order = ['author', 'summary']недостающие поля в
field_orderсохранить их порядок в классе и добавляются после тех, что указаны в списке. В приведенном выше примере будут созданы поля в следующем порядке:['author', 'summary', 'description', 'notes']см. документацию: https://docs.djangoproject.com/en/stable/ref/forms/api/#notes-on-field-ordering
до Django 1.6
у меня была такая же проблема, и я нашел другую технику для переупорядочения полей в Поваренная Книга Джанго:
class EditForm(forms.Form): summary = forms.CharField() description = forms.CharField(widget=forms.TextArea) class CreateForm(EditForm): name = forms.CharField() def __init__(self, *args, **kwargs): super(CreateForm, self).__init__(*args, **kwargs) self.fields.keyOrder = ['name', 'summary', 'description']
из 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
я использовал решение, опубликованное Selene, но обнаружил, что он удалил все поля, которые не были назначены keyOrder. Форма, которую я подкласс имеет много полей, так что это не очень хорошо работает для меня. Я закодировал эту функцию, чтобы решить проблему, используя ответ akaihola, но если вы хотите, чтобы она работала как Selene, все, что вам нужно сделать, это установить
throw_awayдоTrue.def order_fields(form, field_list, throw_away=False): """ Accepts a form and a list of dictionary keys which map to the form's fields. After running the form's fields list will begin with the fields in field_list. If throw_away is set to true only the fields in the field_list will remain in the form. example use: field_list = ['first_name', 'last_name'] order_fields(self, field_list) """ if throw_away: form.fields.keyOrder = field_list else: for field in field_list[::-1]: form.fields.insert(0, field, form.fields.pop(field))вот как я использую его в моем собственном коде:
class NestableCommentForm(ExtendedCommentSecurityForm): # TODO: Have min and max length be determined through settings. comment = forms.CharField(widget=forms.Textarea, max_length=100) parent_id = forms.IntegerField(widget=forms.HiddenInput, required=False) def __init__(self, *args, **kwargs): super(NestableCommentForm, self).__init__(*args, **kwargs) order_fields(self, ['comment', 'captcha'])
похоже, что в какой-то момент базовая структура порядка полей была изменена из конкретного django
SordedDictк стандарту pythonOrderedDictтаким образом, в 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Я уверен, что кто-то может Гольф, что в две или три линии, но для целей S. O. я думаю, ясно показывая, как это работает лучше, чем Кливер.
вы также можете создать декоратор для заказа полей (вдохновленный решением Джошуа):
def order_fields(*field_list): def decorator(form): original_init = form.__init__ def init(self, *args, **kwargs): original_init(self, *args, **kwargs) for field in field_list[::-1]: self.fields.insert(0, field, self.fields.pop(field)) form.__init__ = init return form return decoratorЭто гарантирует, что все поля, переданные декоратору, будут первыми. Вы можете использовать его следующим образом:
@order_fields('name') class CreateForm(EditForm): name = forms.CharField()
подход принятого ответа использует внутренний 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)
смотрите заметки в это так вопрос по пути внутренние устройства Django отслеживают порядок полей; ответы включают предложения о том, как" переупорядочить " поля по своему вкусу (в конце концов это сводится к возиться с ).
альтернативные методы изменения порядка полей:
Pop-and-insert:
self.fields.insert(0, 'name', self.fields.pop('name'))Pop-and-append:
self.fields['summary'] = self.fields.pop('summary') self.fields['description'] = self.fields.pop('description')Pop-and-append-all:
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') ] )но подход Селены из Поваренной книги Django по-прежнему кажется самым ясным.
основанный на ответе @akaihola и обновленный для работы с последним Django 1.5 as
self.fields.insertидет подешевел.from easycontactus.forms import * from django import forms class CustomEasyContactUsForm(EasyContactUsForm): ### form settings and configuration CAPTHCA_PHRASE = 'igolf' ### native methods def __init__(self, *args, **kwargs): super(CustomEasyContactUsForm, self).__init__(*args, **kwargs) # re-order placement of added attachment field self.fields.keyOrder.insert(self.fields.keyOrder.index('captcha'), self.fields.keyOrder.pop(self.fields.keyOrder.index('attachment')) ) ### field defintitions attachment = forms.FileField()в приведенном выше примере мы расширяем базовый класс EasyContactUsForm, как он определен в django-easycontactus пакета.
Я построил форму "ExRegistrationForm", унаследованную от "RegistrationForm" из Django-Registration-Redux. Я столкнулся с двумя проблемами, одна из которых заключалась в переупорядочении полей на странице вывода html после создания новой формы.
Я решил их следующим образом:
1. Вопрос 1: Удалите имя пользователя из регистрационной формы: In 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: Сделать имя и фамилию появляются сверху: в шаблоны/Регистрация/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 %}
Comments