Django 2.2.4.dev20190701100356 文档

表单集

class 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)

迭代 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=2max_num=2 表单集初始化为一个 initial 项目,将显示初始项目的表单和一个空白表单。

如果初始数据中的项数超过 max_num ,将显示所有初始数据窗体,而不管 max_num 不会显示额外的表单。例如,如果 extra=3max_num=1 表单集初始化为两个初始项,将显示两个具有初始数据的表单。

A max_num 价值 None (默认值)对显示的表单数(1000)设置了一个上限。实际上,这相当于没有限制。

默认情况下, max_num 只影响显示的表单数,不影响验证。如果 validate_max=True 传递给 formset_factory() 然后 max_num 将影响验证。见 validate_max .

表单集验证

使用Formset验证几乎与常规的 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()[源代码]

要检查表单集中有多少错误,可以使用 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_FORMSform-INITIAL_FORMSform-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添加新表单,那么您也应该增加该表单中的count字段。另一方面,如果使用javascript允许删除现有对象,则需要确保要删除的对象正确标记为删除,方法是包括 form-#-DELETEPOST 数据。预计所有表单都存在于 POST 不考虑数据。

管理表单作为表单集本身的属性提供。在模板中呈现表单集时,可以通过呈现包括所有管理数据 {{{{ my_formset.management_form }}}} (根据需要替换表单集的名称)。

total_form_count and initial_form_count

BaseFormSet 有一些方法与 ManagementFormtotal_form_countinitial_form_count .

total_form_count 返回此表单集中的表单总数。 initial_form_count 返回表单集中预先填充的表单数,并用于确定需要多少表单。您可能永远不需要重写这两种方法中的任何一种,因此请确保您了解它们在执行之前所做的工作。

empty_form

BaseFormSet 提供附加属性 empty_form 返回前缀为的窗体实例 __prefix__ 以便于在带有JavaScript的动态表单中使用。

自定义表单集验证

一个窗体有一个 clean 方法类似于 Form 类。这是您定义自己在Formset级别工作的验证的地方:

>>> 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 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_ordercan_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() 方法,因为没有删除表单的一般概念。

向表单集添加其他字段

如果需要向表单集添加其他字段,可以轻松完成。Formset基类提供 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 也可能依赖于特定的窗体实例。Formset基类提供 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">

如果你想的话这很有用 use more than one formset in a view .

在视图和模板中使用表单集

在视图中使用表单集与使用常规表单集一样简单 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_deletecan_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>

同样,如果表单集能够 (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案例中,以便正确呈现和处理。

每个窗体的 prefix 替换默认值 form 添加到每个字段的前缀 nameid HTML属性。