Skip to main content

表单

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__() 方法提供备用实施来更改此顺序。

Formsets也可以索引到,返回相应的窗体。如果你重写 __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=[...])

限制表单的最大数量

formset_factory()max_num 参数允许您限制表单集显示的表单数量:

>>> 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 的值大于初始数据中现有项目的数量,则只要表单总数不超过 max_num,最多可将 extra 个附加空白表单添加到表单集中。例如,如果 extra=2max_num=2 和formset用一个 initial 项初始化,则将显示初始项的表单和一个空白表单。

如果初始数据中的项目数超过 max_num,则将显示所有初始数据形式,而不管 max_num 的值,并且不会显示额外的表单。例如,如果 extra=3max_num=1 以及formset用两个初始项初始化,那么将显示具有初始数据的两个形式。

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

我们没有传递任何数据到formset,导致一个有效的形式。表单集足够聪明,可以忽略未更改的其他表单。如果我们提供无效的文章:

>>> 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 是一个列表,其条目对应于formset中的表单。对两种表单中的每一种执行验证,并为第二个项目显示预期的错误消息。

正如使用正常 Form 时,表单集表单中的每个字段可能包含HTML属性,如 maxlength,用于浏览器验证。但是,formets的表单字段将不包括 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

您可能已经注意到上面formset的数据中需要的附加数据(form-TOTAL_FORMSform-INITIAL_FORMSform-MAX_NUM_FORMS)。 ManagementForm 需要此数据。 Formset使用此表单来管理包含在表单集中的表单集合。如果您不提供此管理数据,将会引发异常:

>>> 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来允许删除现有对象,则需要通过在 POST 数据中包含 form-#-DELETE 来确保正在删除的对象被正确标记为删除。预期所有形式存在于 POST 数据中。

管理表单可作为表单集本身的属性。在模板中渲染表单集时,可以通过渲染 {{ my_formset.management_form }} 来包含所有管理数据(替换适当的表单集名称)。

total_form_countinitial_form_count

BaseFormSet 具有与 ManagementFormtotal_form_countinitial_form_count 密切相关的几种方法。

total_form_count 返回此表单集中的表单总数。 initial_form_count 返回formset中预填充的表单数,也用于确定需要多少表单。你可能永远不需要重写这些方法,所以请确保你明白他们做什么之前这样做。

empty_form

BaseFormSet 提供了一个附加属性 empty_form,它返回一个前缀为 __prefix__ 的表单实例,以便在使用JavaScript的动态表单中更容易使用。

自定义表单集验证

表单集具有与 Form 类上的 clean 方法类似的 clean 方法。这是您在表单集级别定义您自己的验证:

>>> 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.']

在所有 Form.clean 方法被调用之后调用formset 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.']

即使超过 max_numvalidate_max=True 也会严格对照 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,以帮助在formets中排序表单和从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-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),对象不会被自动删除。您需要在每个 formset.deleted_objects 上调用 delete() 以实际删除它们:

>>> instances = formset.save(commit=False)
>>> for obj in formset.deleted_objects:
...     obj.delete()

另一方面,如果你使用一个简单的 FormSet,它是由你来处理 formset.deleted_forms,也许在你的formset的 save() 方法,因为没有一般的概念,删除一个表单意味着什么。

向表单集添加其他字段

如果你需要添加额外的字段到formset这可以很容易完成。 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(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>

将自定义参数传递到formset表单

有时您的表单类会使用自定义参数,如 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 还可以取决于特定的表单实例。 formset基类提供了一个 get_form_kwargs 方法。该方法接受单个参数 - 表单集中表单的索引。该指数是 empty_formNone:

>>> 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
New in Django 1.9:

添加了 form_kwargs 参数。

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

在视图中使用formset和使用普通的 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>

上面的结果是在formset类上调用 as_table 方法。

手动呈现的 can_deletecan_order

如果在模板中手动渲染字段,则可以使用 {{ form.DELETE }} 渲染 can_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 }} 来呈现它。

在视图中使用多个formset

如果你喜欢,你可以在视图中使用多个formset。 Formsets从表单中借用了大量的行为。有了这个说,你能够使用 prefix 前缀formset表单字段名称与给定的值,以允许多个formset发送到视图没有名称冲突。让我们来看看这是如何实现的:

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,
    })

然后,您将正常渲染这些表单。重要的是要指出,您需要在POST和非POST情况下传递 prefix,以便正确呈现和处理它们。