Наборы форм
-
class BaseFormSet[source]
Набор форм — это уровень абстракции для работы с несколькими формами на одной странице. Его можно лучше всего сравнить с таблицей данных. Предположим, у вас есть следующая форма:
>>> 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)
Итерация по formset приведет к отображению форм в порядке их создания. Вы можете изменить этот порядок, предоставив альтернативное реализацию метода __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() позволяет ограничить количество форм, которые отобразит набор форм:
>>> 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.
Валидация набора форм
Валидация с набором форм почти идентична валидации обычной 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',
... 'form-MAX_NUM_FORMS': '',
... }
>>> formset = ArticleFormSet(data)
>>> formset.is_valid()
True
Мы передали на набор форм данные без данных, что привело к валидной форме. Набор форм достаточно умён, чтобы игнорировать дополнительные формы, которые не были изменены. Если мы передадим невалидную статью:
>>> data = {
... 'form-TOTAL_FORMS': '2',
... 'form-INITIAL_FORMS': '0',
... 'form-MAX_NUM_FORMS': '',
... '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()[source]
Чтобы проверить, сколько ошибок в наборе форм, мы можем использовать метод 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-MAX_NUM_FORMS': '',
... 'form-0-title': '',
... 'form-0-pub_date': '',
... }
>>> formset = ArticleFormSet(data)
>>> formset.has_changed()
False
Понимание ManagementForm
Возможно, вы заметили дополнительные данные (form-TOTAL_FORMS, form-INITIAL_FORMS и form-MAX_NUM_FORMS), которые были необходимы в данных набора форм выше. Эти данные необходимы для ManagementForm. Эта форма используется набором форм для управления коллекцией форм, содержащихся в наборе форм. Если вы не предоставите эти данные управления, будет возбуждено исключение:
>>> data = {
... 'form-0-title': 'Test',
... 'form-0-pub_date': '',
... }
>>> formset = ArticleFormSet(data)
>>> formset.is_valid()
Traceback (most recent call last):
...
django.forms.utils.ValidationError: ['ManagementForm data is missing or has been tampered with']
Он используется для отслеживания количества экземпляров форм, которые отображаются. Если вы добавляете новые формы через JavaScript, вы также должны увеличивать счётчики в этой форме. С другой стороны, если вы используете JavaScript для разрешения удаления существующих объектов, вам необходимо убедиться, что удаляемые объекты правильно помечены для удаления, включив form-#-DELETE в данные POST. Ожидается, что все формы присутствуют в данных POST независимо.
Форма управления доступна как атрибут самого набора форм. При отображении набора форм в шаблоне вы можете включить все данные управления, отобразив {{ my_formset.management_form }} (заменив имя вашего набора форм соответственно).
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.
Кастомизированная валидация набора форм
Набор форм имеет метод clean, аналогичный методу класса Form. Здесь вы определяете собственную валидацию, которая работает на уровне набора форм:
>>> 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:
... title = form.cleaned_data['title']
... if title in titles:
... raise forms.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-MAX_NUM_FORMS': '',
... '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-MIN_NUM_FORMS': '',
... 'form-MAX_NUM_FORMS': '',
... '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 1 or fewer forms.']
validate_max=True выполняет валидацию относительно max_num строго, даже если max_num было превышено, потому что количество предоставленных начальных данных было избыточным.
Примечание
Независимо от validate_max, если количество форм в наборе данных превышает max_num более чем на 1000, форма не будет валидна, как если бы validate_max было установлено, и, кроме того, будут проверены только первые 1000 форм, превышающих max_num. Остальные будут полностью отброшены. Это делается для защиты от атак с исчерпанием памяти с использованием сфальсифицированных запросов 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-MIN_NUM_FORMS': '',
... 'form-MAX_NUM_FORMS': '',
... '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 3 or more forms.']
Работа с упорядочиванием и удалением форм
formset_factory() предоставляет два необязательных параметра can_order и can_delete, которые помогут с упорядочиванием форм в наборах форм и удалением форм из набора форм.
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-MAX_NUM_FORMS': '',
... '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'}
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-MAX_NUM_FORMS': '',
... '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'}]
Если вы используете 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(BaseArticleFormSet, self).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, **kwargs):
... self.user = kwargs.pop('user')
... super(MyArticleForm, self).__init__(*args, **kwargs)
>>> ArticleFormSet = formset_factory(MyArticleForm)
>>> formset = ArticleFormSet(form_kwargs={'user': request.user})
form_kwargs также может зависеть от конкретного экземпляра формы. Базовый класс набора форм предоставляет метод get_form_kwargs. Метод принимает один аргумент — индекс формы в наборе форм. Индекс равен None для пустой_формы:
>>> from django.forms import BaseFormSet >>> from django.forms import formset_factory >>> class BaseArticleFormSet(BaseFormSet): ... def get_form_kwargs(self, index): ... kwargs = super(BaseArticleFormSet, self).get_form_kwargs(index) ... kwargs['custom_kwarg'] = index ... return kwargs
Добавлен аргумент form_kwargs.
Использование набора форм в представлениях и шаблонах
Использование набора форм внутри представления так же просто, как использование обычного класса 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" action="">
{{ formset.management_form }}
<table>
{% for form in formset %}
{{ form }}
{% endfor %}
</table>
</form>
Однако есть небольшой ярлык для вышеперечисленного, позволяющий самому набору форм обрабатывать форму управления:
<form method="post" action="">
<table>
{{ formset }}
</table>
</form>
Вышеупомянутое в итоге вызывает метод as_table в классе набора форм.
Вручную рендеринг can_delete и can_order
Если вы вручную рендерите поля в шаблоне, вы можете рендерить параметр can_delete с помощью {{ form.DELETE }}:
<form method="post" action="">
{{ 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>
Аналогично, если набор форм имеет возможность упорядочивания (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, чтобы он рендерился и обрабатывался правильно.
© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/1.10/topics/forms/formsets/