Наборы форм ¶
-
класс
BaseFormSet
¶
Набор форм - это уровень абстракции для работы с несколькими формами на одной странице. Лучше всего это сравнить с сеткой данных. Допустим, у вас есть следующая форма:
>>> from django import forms
>>> class ArticleForm(forms.Form):
... title = forms.CharField()
... pub_date = forms.DateField()
Вы можете разрешить пользователю создавать сразу несколько статей. Чтобы создать набор форм из ArticleForm
:
>>> from django.forms import formset_factory
>>> ArticleFormSet = formset_factory(ArticleForm)
Теперь вы создали класс набора форм с именем ArticleFormSet
. Создание экземпляра набора форм дает вам возможность перебирать формы в наборе форм и отображать их, как в обычной форме:
>>> formset = ArticleFormSet()
>>> for form in formset:
... print(form.as_table())
<tr><th><label for="id_form-0-title">Title:</label></th><td><input type="text" name="form-0-title" id="id_form-0-title"></td></tr>
<tr><th><label for="id_form-0-pub_date">Pub date:</label></th><td><input type="text" name="form-0-pub_date" id="id_form-0-pub_date"></td></tr>
Как видите, отображается только одна пустая форма. Количество отображаемых пустых форм контролируется extra
параметром. По умолчанию
formset_factory()
определяет одну дополнительную форму; в следующем примере будет создан класс набора форм для отображения двух пустых форм:
>>> ArticleFormSet = formset_factory(ArticleForm, extra=2)
Итерация по набору форм будет отображать формы в том порядке, в котором они были созданы. Вы можете изменить этот порядок, предоставив альтернативную реализацию __iter__()
метода.
Наборы форм также можно индексировать, что возвращает соответствующую форму. Если вы переопределите __iter__
, вам также нужно будет переопределить, __getitem__
чтобы иметь соответствующее поведение.
Использование исходных данных с набором форм ¶
Исходные данные - это то, что определяет основное удобство использования набора форм. Как показано выше, вы можете определить количество дополнительных форм. Это означает, что вы сообщаете набору форм, сколько дополнительных форм нужно показать в дополнение к количеству форм, которые он генерирует из исходных данных. Давайте посмотрим на пример:
>>> import datetime
>>> from django.forms import formset_factory
>>> from myapp.forms import ArticleForm
>>> ArticleFormSet = formset_factory(ArticleForm, extra=2)
>>> formset = ArticleFormSet(initial=[
... {'title': 'Django is now open source',
... 'pub_date': datetime.date.today(),}
... ])
>>> for form in formset:
... print(form.as_table())
<tr><th><label for="id_form-0-title">Title:</label></th><td><input type="text" name="form-0-title" value="Django is now open source" id="id_form-0-title"></td></tr>
<tr><th><label for="id_form-0-pub_date">Pub date:</label></th><td><input type="text" name="form-0-pub_date" value="2008-05-12" id="id_form-0-pub_date"></td></tr>
<tr><th><label for="id_form-1-title">Title:</label></th><td><input type="text" name="form-1-title" id="id_form-1-title"></td></tr>
<tr><th><label for="id_form-1-pub_date">Pub date:</label></th><td><input type="text" name="form-1-pub_date" id="id_form-1-pub_date"></td></tr>
<tr><th><label for="id_form-2-title">Title:</label></th><td><input type="text" name="form-2-title" id="id_form-2-title"></td></tr>
<tr><th><label for="id_form-2-pub_date">Pub date:</label></th><td><input type="text" name="form-2-pub_date" id="id_form-2-pub_date"></td></tr>
Выше показаны три формы. Один для исходных данных, которые были переданы, и две дополнительные формы. Также обратите внимание, что мы передаем список словарей в качестве исходных данных.
Если вы используете initial
для отображения набора форм, вы должны передать то же самое
initial
при обработке отправки этого набора форм, чтобы набор форм мог определить, какие формы были изменены пользователем. Например, вы могли бы иметь что - то вроде: .ArticleFormSet(request.POST, initial=[...])
Смотрите также
Создание наборов форм из моделей с модельными наборами форм .
Ограничение максимального количества форм ¶
max_num
Параметр formset_factory()
дает возможность ограничить количество форм formset будет отображаться:
>>> from django.forms import formset_factory
>>> from myapp.forms import ArticleForm
>>> ArticleFormSet = formset_factory(ArticleForm, extra=2, max_num=1)
>>> formset = ArticleFormSet()
>>> for form in formset:
... print(form.as_table())
<tr><th><label for="id_form-0-title">Title:</label></th><td><input type="text" name="form-0-title" id="id_form-0-title"></td></tr>
<tr><th><label for="id_form-0-pub_date">Pub date:</label></th><td><input type="text" name="form-0-pub_date" id="id_form-0-pub_date"></td></tr>
Если значение max_num
больше, чем количество существующих элементов в исходных данных, до extra
дополнительных пустых форм будут добавлены в набор форм, если общее количество форм не превышает max_num
. Например, если extra=2
и max_num=2
и набор форм инициализирован одним initial
элементом, будет отображаться форма для исходного элемента и одна пустая форма.
Если количество элементов в исходных данных превышает max_num
, все формы исходных данных будут отображаться независимо от значения, max_num
и никакие дополнительные формы отображаться не будут. Например, если extra=3
и max_num=1
и набор форм инициализируется двумя начальными элементами, будут отображены две формы с начальными данными.
max_num
Значение None
(по умолчанию) ставит верхний предел на количество форм , отображаемого (1000). На практике это эквивалентно неограниченному количеству.
По умолчанию max_num
влияет только на количество отображаемых форм и не влияет на проверку. Если validate_max=True
передается в
formset_factory()
, то max_num
это повлияет на проверку. См. Validate_max .
Ограничение максимального количества экземпляров форм ¶
absolute_max
Параметр formset_factory()
позволяет ограничить количество форм , которые могут быть созданы при подаче POST
данных. Это защищает от атак нехватки памяти с использованием поддельных POST
запросов:
>>> from django.forms.formsets import formset_factory
>>> from myapp.forms import ArticleForm
>>> ArticleFormSet = formset_factory(ArticleForm, absolute_max=1500)
>>> data = {
... 'form-TOTAL_FORMS': '1501',
... 'form-INITIAL_FORMS': '0',
... }
>>> formset = ArticleFormSet(data)
>>> len(formset.forms)
1500
>>> formset.is_valid()
False
>>> formset.non_form_errors()
['Please submit at most 1000 forms.']
Когда absolute_max
есть None
, по умолчанию . (Если
есть , то по умолчанию ).max_num + 1000
max_num
None
2000
Если absolute_max
меньше max_num
, ValueError
будет увеличиваться.
Проверка набора форм ¶
Проверка с помощью набора форм практически идентична обычной Form
. В is_valid
наборе форм есть метод, обеспечивающий удобный способ проверки всех форм в наборе форм:
>>> from django.forms import formset_factory
>>> from myapp.forms import ArticleForm
>>> ArticleFormSet = formset_factory(ArticleForm)
>>> data = {
... 'form-TOTAL_FORMS': '1',
... 'form-INITIAL_FORMS': '0',
... }
>>> formset = ArticleFormSet(data)
>>> formset.is_valid()
True
Мы не передали никаких данных в набор форм, что привело к правильной форме. Набор форм достаточно умен, чтобы игнорировать лишние формы, которые не были изменены. Если мы предоставим недействительную статью:
>>> data = {
... 'form-TOTAL_FORMS': '2',
... 'form-INITIAL_FORMS': '0',
... 'form-0-title': 'Test',
... 'form-0-pub_date': '1904-06-16',
... 'form-1-title': 'Test',
... 'form-1-pub_date': '', # <-- this date is missing but required
... }
>>> formset = ArticleFormSet(data)
>>> formset.is_valid()
False
>>> formset.errors
[{}, {'pub_date': ['This field is required.']}]
Как мы видим, formset.errors
это список, записи которого соответствуют формам в наборе форм. Проверка была выполнена для каждой из двух форм, и для второго элемента отображается ожидаемое сообщение об ошибке.
Как и при использовании обычного Form
, каждое поле в формах набора форм может включать атрибуты HTML, например, maxlength
для проверки браузера. Однако поля форм наборов форм не будут включать required
атрибут, так как эта проверка может быть неправильной при добавлении и удалении форм.
-
BaseFormSet.
total_error_count
() ¶
Чтобы проверить, сколько ошибок в наборе форм, мы можем использовать
total_error_count
метод:
>>> # Using the previous example
>>> formset.errors
[{}, {'pub_date': ['This field is required.']}]
>>> len(formset.errors)
2
>>> formset.total_error_count()
1
Мы также можем проверить, отличаются ли данные формы от исходных данных (т.е. форма была отправлена без данных):
>>> data = {
... 'form-TOTAL_FORMS': '1',
... 'form-INITIAL_FORMS': '0',
... 'form-0-title': '',
... 'form-0-pub_date': '',
... }
>>> formset = ArticleFormSet(data)
>>> formset.has_changed()
False
Понимание ManagementForm
¶
Возможно, вы заметили дополнительные данные ( form-TOTAL_FORMS
,
form-INITIAL_FORMS
), которые требовались в приведенных выше данных набора форм. Эти данные необходимы для ManagementForm
. Эта форма используется набором форм для управления коллекцией форм, содержащихся в наборе форм. Если вы не предоставите эти данные управления, набор форм будет недействительным:
>>> data = {
... 'form-0-title': 'Test',
... 'form-0-pub_date': '',
... }
>>> formset = ArticleFormSet(data)
>>> formset.is_valid()
False
Он используется для отслеживания количества отображаемых экземпляров форм. Если вы добавляете новые формы через JavaScript, вам также следует увеличивать количество полей в этой форме. С другой стороны, если вы используете JavaScript, чтобы разрешить удаление существующих объектов, вам необходимо убедиться, что удаляемые правильно помечены для удаления, включив их form-#-DELETE
в POST
данные. Ожидается, что все формы присутствуют в POST
данных независимо.
Форма управления доступна как атрибут самого набора форм. При визуализации набора форм в шаблоне вы можете включить все данные управления путем визуализации
(при необходимости подставляя имя набора форм).{{ my_formset.management_form }}
Примечание
Так же , как form-TOTAL_FORMS
и form-INITIAL_FORMS
поле , показанное в примерах здесь, форма управления также включает в себя
form-MIN_NUM_FORMS
и form-MAX_NUM_FORMS
поле. Они выводятся вместе с остальной частью формы управления, но только для удобства клиентского кода. Эти поля не являются обязательными и поэтому не показаны в POST
данных примера .
formset.is_valid()
теперь возвращается, False
а не вызывает исключение, когда форма управления отсутствует или была изменена.
total_form_count
и initial_form_count
¶
BaseFormSet
имеет несколько методов, которые тесно связаны с
ManagementForm
, total_form_count
и initial_form_count
.
total_form_count
возвращает общее количество форм в этом наборе форм.
initial_form_count
возвращает количество форм в наборе форм, которые были предварительно заполнены, а также используется для определения необходимого количества форм. Вам, вероятно, никогда не понадобится отменять любой из этих методов, поэтому, пожалуйста, убедитесь, что вы понимаете, что они делают, прежде чем делать это.
empty_form
¶
BaseFormSet
предоставляет дополнительный атрибут, empty_form
который возвращает экземпляр формы с префиксом __prefix__
для упрощения использования в динамических формах с помощью JavaScript.
error_messages
¶
error_messages
Аргумент позволяет переопределить сообщения по умолчанию , что formset поднимет. Передайте словарь с ключами, соответствующими сообщениям об ошибках, которые вы хотите переопределить. Например, вот сообщение об ошибке по умолчанию, когда форма управления отсутствует:
>>> formset = ArticleFormSet({})
>>> formset.is_valid()
False
>>> formset.non_form_errors()
['ManagementForm data is missing or has been tampered with. Missing fields: form-TOTAL_FORMS, form-INITIAL_FORMS. You may need to file a bug report if the issue persists.']
А вот собственное сообщение об ошибке:
>>> formset = ArticleFormSet({}, error_messages={'missing_management_form': 'Sorry, something went wrong.'})
>>> formset.is_valid()
False
>>> formset.non_form_errors()
['Sorry, something went wrong.']
Проверка пользовательского набора форм ¶
В наборе форм есть clean
метод, аналогичный методу Form
класса. Здесь вы определяете свою собственную проверку, которая работает на уровне набора форм:
>>> from django.core.exceptions import ValidationError
>>> from django.forms import BaseFormSet
>>> from django.forms import formset_factory
>>> from myapp.forms import ArticleForm
>>> class BaseArticleFormSet(BaseFormSet):
... def clean(self):
... """Checks that no two articles have the same title."""
... if any(self.errors):
... # Don't bother validating the formset unless each form is valid on its own
... return
... titles = []
... for form in self.forms:
... if self.can_delete and self._should_delete_form(form):
... continue
... title = form.cleaned_data.get('title')
... if title in titles:
... raise ValidationError("Articles in a set must have distinct titles.")
... titles.append(title)
>>> ArticleFormSet = formset_factory(ArticleForm, formset=BaseArticleFormSet)
>>> data = {
... 'form-TOTAL_FORMS': '2',
... 'form-INITIAL_FORMS': '0',
... 'form-0-title': 'Test',
... 'form-0-pub_date': '1904-06-16',
... 'form-1-title': 'Test',
... 'form-1-pub_date': '1912-06-23',
... }
>>> formset = ArticleFormSet(data)
>>> formset.is_valid()
False
>>> formset.errors
[{}, {}]
>>> formset.non_form_errors()
['Articles in a set must have distinct titles.']
Метод набора форм clean
вызывается после того Form.clean
, как были вызваны все методы. Ошибки будут обнаружены с помощью non_form_errors()
метода в наборе форм.
Проверка количества форм в наборе форм ¶
Django предоставляет несколько способов проверить минимальное или максимальное количество отправленных форм. Приложения, которым требуется более настраиваемая проверка количества форм, должны использовать настраиваемую проверку набора форм.
validate_max
¶
Если validate_max=True
передано
formset_factory()
, проверка также проверит, что количество форм в наборе данных, за вычетом помеченных для удаления, меньше или равно max_num
.
>>> from django.forms import formset_factory
>>> from myapp.forms import ArticleForm
>>> ArticleFormSet = formset_factory(ArticleForm, max_num=1, validate_max=True)
>>> data = {
... 'form-TOTAL_FORMS': '2',
... 'form-INITIAL_FORMS': '0',
... 'form-0-title': 'Test',
... 'form-0-pub_date': '1904-06-16',
... 'form-1-title': 'Test 2',
... 'form-1-pub_date': '1912-06-23',
... }
>>> formset = ArticleFormSet(data)
>>> formset.is_valid()
False
>>> formset.errors
[{}, {}]
>>> formset.non_form_errors()
['Please submit at most 1 form.']
validate_max=True
max_num
строго проверяет соответствие, даже если
max_num
было превышено из-за чрезмерного количества предоставленных исходных данных.
Примечание
Независимо от того validate_max
, если количество форм в наборе данных превышает absolute_max
, тогда форма не
сможет пройти проверку, как если бы она
validate_max
была установлена, и, кроме того, absolute_max
будут проверены только первые формы. Остаток будет полностью усечен. Это сделано для защиты от атак нехватки памяти с использованием поддельных запросов POST. См. Раздел Ограничение максимального количества экземпляров форм .
validate_min
¶
Если validate_min=True
передано
formset_factory()
, проверка также проверит, что количество форм в наборе данных, за вычетом помеченных для удаления, больше или равно min_num
.
>>> from django.forms import formset_factory
>>> from myapp.forms import ArticleForm
>>> ArticleFormSet = formset_factory(ArticleForm, min_num=3, validate_min=True)
>>> data = {
... 'form-TOTAL_FORMS': '2',
... 'form-INITIAL_FORMS': '0',
... 'form-0-title': 'Test',
... 'form-0-pub_date': '1904-06-16',
... 'form-1-title': 'Test 2',
... 'form-1-pub_date': '1912-06-23',
... }
>>> formset = ArticleFormSet(data)
>>> formset.is_valid()
False
>>> formset.errors
[{}, {}]
>>> formset.non_form_errors()
['Please submit at least 3 forms.']
Примечание
Независимо от того validate_min
, если набор форм не содержит данных,
будут отображаться пустые формы.extra + min_num
Работа с заказом и удалением форм ¶
formset_factory()
Обеспечивает два дополнительных параметров can_order
и can_delete
помочь с упорядочением форм в FormSets и удаление форм из formset.
can_order
¶
-
BaseFormSet.
can_order
¶
По умолчанию: False
Позволяет создать набор форм с возможностью заказа:
>>> from django.forms import formset_factory
>>> from myapp.forms import ArticleForm
>>> ArticleFormSet = formset_factory(ArticleForm, can_order=True)
>>> formset = ArticleFormSet(initial=[
... {'title': 'Article #1', 'pub_date': datetime.date(2008, 5, 10)},
... {'title': 'Article #2', 'pub_date': datetime.date(2008, 5, 11)},
... ])
>>> for form in formset:
... print(form.as_table())
<tr><th><label for="id_form-0-title">Title:</label></th><td><input type="text" name="form-0-title" value="Article #1" id="id_form-0-title"></td></tr>
<tr><th><label for="id_form-0-pub_date">Pub date:</label></th><td><input type="text" name="form-0-pub_date" value="2008-05-10" id="id_form-0-pub_date"></td></tr>
<tr><th><label for="id_form-0-ORDER">Order:</label></th><td><input type="number" name="form-0-ORDER" value="1" id="id_form-0-ORDER"></td></tr>
<tr><th><label for="id_form-1-title">Title:</label></th><td><input type="text" name="form-1-title" value="Article #2" id="id_form-1-title"></td></tr>
<tr><th><label for="id_form-1-pub_date">Pub date:</label></th><td><input type="text" name="form-1-pub_date" value="2008-05-11" id="id_form-1-pub_date"></td></tr>
<tr><th><label for="id_form-1-ORDER">Order:</label></th><td><input type="number" name="form-1-ORDER" value="2" id="id_form-1-ORDER"></td></tr>
<tr><th><label for="id_form-2-title">Title:</label></th><td><input type="text" name="form-2-title" id="id_form-2-title"></td></tr>
<tr><th><label for="id_form-2-pub_date">Pub date:</label></th><td><input type="text" name="form-2-pub_date" id="id_form-2-pub_date"></td></tr>
<tr><th><label for="id_form-2-ORDER">Order:</label></th><td><input type="number" name="form-2-ORDER" id="id_form-2-ORDER"></td></tr>
Это добавляет дополнительное поле к каждой форме. Это новое поле называется ORDER
и является forms.IntegerField
. Формам, полученным из исходных данных, автоматически присваивается числовое значение. Давайте посмотрим, что произойдет, когда пользователь изменит эти значения:
>>> data = {
... 'form-TOTAL_FORMS': '3',
... 'form-INITIAL_FORMS': '2',
... 'form-0-title': 'Article #1',
... 'form-0-pub_date': '2008-05-10',
... 'form-0-ORDER': '2',
... 'form-1-title': 'Article #2',
... 'form-1-pub_date': '2008-05-11',
... 'form-1-ORDER': '1',
... 'form-2-title': 'Article #3',
... 'form-2-pub_date': '2008-05-01',
... 'form-2-ORDER': '0',
... }
>>> formset = ArticleFormSet(data, initial=[
... {'title': 'Article #1', 'pub_date': datetime.date(2008, 5, 10)},
... {'title': 'Article #2', 'pub_date': datetime.date(2008, 5, 11)},
... ])
>>> formset.is_valid()
True
>>> for form in formset.ordered_forms:
... print(form.cleaned_data)
{'pub_date': datetime.date(2008, 5, 1), 'ORDER': 0, 'title': 'Article #3'}
{'pub_date': datetime.date(2008, 5, 11), 'ORDER': 1, 'title': 'Article #2'}
{'pub_date': datetime.date(2008, 5, 10), 'ORDER': 2, 'title': 'Article #1'}
BaseFormSet
также предоставляет
ordering_widget
атрибут и
get_ordering_widget()
метод, которые управляют используемым виджетом
can_order
.
ordering_widget
¶
-
BaseFormSet.
ordering_widget
¶
По умолчанию: NumberInput
Установите, ordering_widget
чтобы указать класс виджета, который будет использоваться с
can_order
:
>>> from django.forms import BaseFormSet, formset_factory
>>> from myapp.forms import ArticleForm
>>> class BaseArticleFormSet(BaseFormSet):
... ordering_widget = HiddenInput
>>> ArticleFormSet = formset_factory(ArticleForm, formset=BaseArticleFormSet, can_order=True)
get_ordering_widget
¶
-
BaseFormSet.
get_ordering_widget
() ¶
Переопределите, get_ordering_widget()
если вам нужно предоставить экземпляр виджета для использования с can_order
:
>>> from django.forms import BaseFormSet, formset_factory
>>> from myapp.forms import ArticleForm
>>> class BaseArticleFormSet(BaseFormSet):
... def get_ordering_widget(self):
... return HiddenInput(attrs={'class': 'ordering'})
>>> ArticleFormSet = formset_factory(ArticleForm, formset=BaseArticleFormSet, can_order=True)
can_delete
¶
-
BaseFormSet.
can_delete
¶
По умолчанию: False
Позволяет создать набор форм с возможностью выбора форм для удаления:
>>> from django.forms import formset_factory
>>> from myapp.forms import ArticleForm
>>> ArticleFormSet = formset_factory(ArticleForm, can_delete=True)
>>> formset = ArticleFormSet(initial=[
... {'title': 'Article #1', 'pub_date': datetime.date(2008, 5, 10)},
... {'title': 'Article #2', 'pub_date': datetime.date(2008, 5, 11)},
... ])
>>> for form in formset:
... print(form.as_table())
<tr><th><label for="id_form-0-title">Title:</label></th><td><input type="text" name="form-0-title" value="Article #1" id="id_form-0-title"></td></tr>
<tr><th><label for="id_form-0-pub_date">Pub date:</label></th><td><input type="text" name="form-0-pub_date" value="2008-05-10" id="id_form-0-pub_date"></td></tr>
<tr><th><label for="id_form-0-DELETE">Delete:</label></th><td><input type="checkbox" name="form-0-DELETE" id="id_form-0-DELETE"></td></tr>
<tr><th><label for="id_form-1-title">Title:</label></th><td><input type="text" name="form-1-title" value="Article #2" id="id_form-1-title"></td></tr>
<tr><th><label for="id_form-1-pub_date">Pub date:</label></th><td><input type="text" name="form-1-pub_date" value="2008-05-11" id="id_form-1-pub_date"></td></tr>
<tr><th><label for="id_form-1-DELETE">Delete:</label></th><td><input type="checkbox" name="form-1-DELETE" id="id_form-1-DELETE"></td></tr>
<tr><th><label for="id_form-2-title">Title:</label></th><td><input type="text" name="form-2-title" id="id_form-2-title"></td></tr>
<tr><th><label for="id_form-2-pub_date">Pub date:</label></th><td><input type="text" name="form-2-pub_date" id="id_form-2-pub_date"></td></tr>
<tr><th><label for="id_form-2-DELETE">Delete:</label></th><td><input type="checkbox" name="form-2-DELETE" id="id_form-2-DELETE"></td></tr>
Аналогично can_order
это добавляет новое поле в каждой названной форме DELETE
и является forms.BooleanField
. Когда данные поступают через пометку любого из полей удаления, вы можете получить к ним доступ с помощью deleted_forms
:
>>> data = {
... 'form-TOTAL_FORMS': '3',
... 'form-INITIAL_FORMS': '2',
... 'form-0-title': 'Article #1',
... 'form-0-pub_date': '2008-05-10',
... 'form-0-DELETE': 'on',
... 'form-1-title': 'Article #2',
... 'form-1-pub_date': '2008-05-11',
... 'form-1-DELETE': '',
... 'form-2-title': '',
... 'form-2-pub_date': '',
... 'form-2-DELETE': '',
... }
>>> formset = ArticleFormSet(data, initial=[
... {'title': 'Article #1', 'pub_date': datetime.date(2008, 5, 10)},
... {'title': 'Article #2', 'pub_date': datetime.date(2008, 5, 11)},
... ])
>>> [form.cleaned_data for form in formset.deleted_forms]
[{'DELETE': True, 'pub_date': datetime.date(2008, 5, 10), 'title': 'Article #1'}]
Если вы используете a ModelFormSet
, экземпляры модели для удаленных форм будут удалены при вызове
formset.save()
.
Если вы позвоните formset.save(commit=False)
, объекты не будут удалены автоматически. Вам нужно будет вызвать delete()
каждый из них,
formset.deleted_objects
чтобы удалить их:
>>> instances = formset.save(commit=False)
>>> for obj in formset.deleted_objects:
... obj.delete()
С другой стороны, если вы используете простую форму FormSet
, вам решать formset.deleted_forms
, возможно, в методе вашего набора форм save()
, поскольку нет общего представления о том, что означает удаление формы.
Добавление дополнительных полей в набор форм ¶
Если вам нужно добавить дополнительные поля в набор форм, это легко сделать. Базовый класс набора форм предоставляет add_fields
метод. Вы можете переопределить этот метод, чтобы добавить свои собственные поля или даже переопределить поля / атрибуты по умолчанию для полей порядка и удаления:
>>> from django.forms import BaseFormSet
>>> from django.forms import formset_factory
>>> from myapp.forms import ArticleForm
>>> class BaseArticleFormSet(BaseFormSet):
... def add_fields(self, form, index):
... super().add_fields(form, index)
... form.fields["my_field"] = forms.CharField()
>>> ArticleFormSet = formset_factory(ArticleForm, formset=BaseArticleFormSet)
>>> formset = ArticleFormSet()
>>> for form in formset:
... print(form.as_table())
<tr><th><label for="id_form-0-title">Title:</label></th><td><input type="text" name="form-0-title" id="id_form-0-title"></td></tr>
<tr><th><label for="id_form-0-pub_date">Pub date:</label></th><td><input type="text" name="form-0-pub_date" id="id_form-0-pub_date"></td></tr>
<tr><th><label for="id_form-0-my_field">My field:</label></th><td><input type="text" name="form-0-my_field" id="id_form-0-my_field"></td></tr>
Передача пользовательских параметров в формы набора форм ¶
Иногда ваш класс формы принимает специальные параметры, например MyArticleForm
. Вы можете передать этот параметр при создании экземпляра набора форм:
>>> from django.forms import BaseFormSet
>>> from django.forms import formset_factory
>>> from myapp.forms import ArticleForm
>>> class MyArticleForm(ArticleForm):
... def __init__(self, *args, user, **kwargs):
... self.user = user
... super().__init__(*args, **kwargs)
>>> ArticleFormSet = formset_factory(MyArticleForm)
>>> formset = ArticleFormSet(form_kwargs={'user': request.user})
Также form_kwargs
может зависеть от конкретного экземпляра формы. Базовый класс набора форм предоставляет get_form_kwargs
метод. Метод принимает единственный аргумент - индекс формы в наборе форм. Индекс предназначен None
для
empty_form :
>>> from django.forms import BaseFormSet
>>> from django.forms import formset_factory
>>> class BaseArticleFormSet(BaseFormSet):
... def get_form_kwargs(self, index):
... kwargs = super().get_form_kwargs(index)
... kwargs['custom_kwarg'] = index
... return kwargs
Настройка префикса набора форм ¶
В отображаемом HTML наборы форм включают префикс для имени каждого поля. По умолчанию это префикс 'form'
, но его можно настроить с помощью prefix
аргумента набора форм
.
Например, в случае по умолчанию вы можете увидеть:
<label for="id_form-0-title">Title:</label>
<input type="text" name="form-0-title" id="id_form-0-title">
Но с ArticleFormset(prefix='article')
этим становится:
<label for="id_article-0-title">Title:</label>
<input type="text" name="article-0-title" id="id_article-0-title">
Это полезно, если вы хотите использовать более одного набора форм в представлении .
Использование набора форм в представлениях и шаблонах ¶
Использование набора форм внутри представления не сильно отличается от использования обычного
Form
класса. Единственное, о чем вам нужно знать, - это обязательно использовать форму управления внутри шаблона. Давайте посмотрим на образец представления:
from django.forms import formset_factory
from django.shortcuts import render
from myapp.forms import ArticleForm
def manage_articles(request):
ArticleFormSet = formset_factory(ArticleForm)
if request.method == 'POST':
formset = ArticleFormSet(request.POST, request.FILES)
if formset.is_valid():
# do something with the formset.cleaned_data
pass
else:
formset = ArticleFormSet()
return render(request, 'manage_articles.html', {'formset': formset})
manage_articles.html
Шаблон может выглядеть следующим образом :
<form method="post">
{{ formset.management_form }}
<table>
{% for form in formset %}
{{ form }}
{% endfor %}
</table>
</form>
Однако есть небольшое сокращение для вышеупомянутого, позволяя самому набору форм работать с формой управления:
<form method="post">
<table>
{{ formset }}
</table>
</form>
Вышеупомянутое заканчивается вызовом as_table
метода в классе набора форм.
Рендеринг вручную can_delete
и can_order
¶
Если вы вручную визуализируете поля в шаблоне, вы можете визуализировать
can_delete
параметр с помощью :{{ form.DELETE }}
<form method="post">
{{ formset.management_form }}
{% for form in formset %}
<ul>
<li>{{ form.title }}</li>
<li>{{ form.pub_date }}</li>
{% if formset.can_delete %}
<li>{{ form.DELETE }}</li>
{% endif %}
</ul>
{% endfor %}
</form>
Точно так же, если набор форм имеет возможность order ( can_order=True
), его можно отобразить с помощью .{{ form.ORDER }}
Использование нескольких наборов форм в представлении ¶
Вы можете использовать более одного набора форм в представлении, если хотите. Наборы форм заимствуют большую часть своего поведения у форм. С учетом сказанного вы можете использовать
prefix
для префикса имен полей формы набора форм с заданным значением, чтобы разрешить отправку более одного набора форм в представление без конфликта имен. Давайте посмотрим, как это можно сделать:
from django.forms import formset_factory
from django.shortcuts import render
from myapp.forms import ArticleForm, BookForm
def manage_articles(request):
ArticleFormSet = formset_factory(ArticleForm)
BookFormSet = formset_factory(BookForm)
if request.method == 'POST':
article_formset = ArticleFormSet(request.POST, request.FILES, prefix='articles')
book_formset = BookFormSet(request.POST, request.FILES, prefix='books')
if article_formset.is_valid() and book_formset.is_valid():
# do something with the cleaned_data on the formsets.
pass
else:
article_formset = ArticleFormSet(prefix='articles')
book_formset = BookFormSet(prefix='books')
return render(request, 'manage_articles.html', {
'article_formset': article_formset,
'book_formset': book_formset,
})
Затем вы должны отрендерить наборы форм как обычно. Важно отметить, что вам необходимо передавать prefix
как POST, так и не-POST случаи, чтобы они отображались и обрабатывались правильно.
Префикс каждого набора форм заменяет префикс по умолчанию form
, добавляемый к каждому полю name
и id
атрибутам HTML.