表单集¶ T0>

BaseFormSet[source]

formset是一个抽象层,可以在同一页面上使用多个表单。 它可以与数据网格进行比较。 假设你有以下形式:

>>> from django import forms
>>> class ArticleForm(forms.Form):
...     title = forms.CharField()
...     pub_date = forms.DateField()

您可能希望允许用户一次创建多个文章。 要从ArticleForm创建一个formset,您可以这样做:

>>> from django.forms import formset_factory
>>> ArticleFormSet = formset_factory(ArticleForm)

您现在已经创建了一个名为ArticleFormSet的表单集。 formset使您能够遍历formset中的表单,并像使用常规表单一样显示它们:

>>> 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参数控制。 By default, formset_factory() defines one extra form; the following example will display two blank forms:

>>> ArticleFormSet = formset_factory(ArticleForm, extra=2)

遍历formset将按照创建的顺序呈现表单。 您可以通过为__iter__()方法提供替代实现来更改此顺序。

Formset也可以被索引到,它返回相应的表单。 如果覆盖__iter__,则还需要覆盖__getitem__以具有匹配的行为。

使用formset 的初始数据

初始数据是驱动formset的主要可用性的原因。 如上所示,您可以定义额外表格的数量。 这意味着,除了从初始数据生成的表单数量之外,您还要告诉表单集除了要显示多少附加表单。 我们来看一个例子:

>>> 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来显示formset,则在处理formset的提交时应该传递相同的initial,以便formset可以检测用户更改了哪些表单。 例如,你可能有这样的:ArticleFormSet(request.POST, initial = [...])

也可以看看

Creating formsets from models with model formsets从模型创建表单集。

限制表单的最大数量

The max_num parameter to formset_factory() gives you the ability to limit the number of forms the formset will display:

>>> 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的其他空白表单将被添加到formset中,只要总数表单数量不超过max_num 例如,如果extra=2max_num=2并且formset用一个initial项初始化,则初始项和一个将显示空白表单。

如果初始数据中的项目数量超过了max_num,则无论max_num的值如何,都将显示所有初始数据表单,并且不会显示额外的表单。 例如,如果extra=3max_num=1并且formset用两个初始项初始化,则将显示具有初始数据的两个表单。

A max_num value of None (the default) puts a high limit on the number of forms displayed (1000). 实际上这相当于没有限制。

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

Formset验证

使用formset进行验证几乎与普通的Form完全相同。 在formset上有一个is_valid方法来提供一个方便的方法来验证formset中的所有表单:

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

As we can see, formset.errors is a list whose entries correspond to the forms in the formset. 对两种形式中的每种形式都进行了验证,第二项出现预期的错误消息。

就像使用普通的Form时一样,formset表单中的每个字段都可以包含HTML属性,例如用于浏览器验证的maxlength 但是,表单集的表单字段将不包含required属性,因为在添加和删除表单时验证可能不正确。

BaseFormSet。total_error_count()[source]

要检查formset中有多少个错误,我们可以使用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

You may have noticed the additional data (form-TOTAL_FORMS, form-INITIAL_FORMS and form-MAX_NUM_FORMS) that was required in the formset’s data above. 这个数据是ManagementForm所必需的。 该表单由formset用来管理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添加新的表单,那么您应该以这种形式增加计数字段。 On the other hand, if you are using JavaScript to allow deletion of existing objects, then you need to ensure the ones being removed are properly marked for deletion by including form-#-DELETE in the POST data. 预计所有表单都存在于POST数据中。

管理表单可作为formset本身的一个属性。 在模板中渲染formset时,可以通过渲染{{ my_formset.management_form }}来包含所有管理数据。 t0>(根据需要替换您的formset的名称)。

total_form_countinitial_form_count

BaseFormSet has a couple of methods that are closely related to the ManagementForm, total_form_count and initial_form_count.

total_form_count returns the total number of forms in this formset. initial_form_count returns the number of forms in the formset that were pre-filled, and is also used to determine how many forms are required. 你可能永远不需要重写这些方法,所以请确保你明白他们在做什么之前做了什么。

empty_form

BaseFormSet provides an additional attribute empty_form which returns a form instance with a prefix of __prefix__ for easier use in dynamic forms with JavaScript.

自定义表单验证

formset有一个类似于Form类的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方法。 使用formset上的non_form_errors()方法可以找到错误。

验证formset 中的表单数目

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 validates against max_num strictly even if max_num was exceeded because the amount of initial data supplied was excessive.

注意

Regardless of validate_max, if the number of forms in a data set exceeds max_num by more than 1000, then the form will fail to validate as if validate_max were set, and additionally only the first 1000 forms above max_num will be validated. 其余部分将被完全截断。 这是为了防止使用伪造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.']

处理表单的排序和删除

The formset_factory() provides two optional parameters can_order and can_delete to help with ordering of forms in formsets and deletion of forms from a formset.

can_order

BaseFormSet。 can_order T0> ¶ T1>

默认:False

让您创建一个能够订购的formset:

>>> 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 T0> ¶ T1>

默认: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()时,已删除表单的模型实例将被删除。

If you call formset.save(commit=False), objects will not be deleted automatically. 您需要在每个formset.deleted_objects上调用delete()以实际删除它们:

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

On the other hand, if you are using a plain FormSet, it’s up to you to handle formset.deleted_forms, perhaps in your formset’s save() method, as there’s no general notion of what it means to delete a form.

将其他字段添加到formset

如果您需要添加额外的字段到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().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表单

Sometimes your form class takes custom parameters, like MyArticleForm. 实例化formset时可以传递此参数:

>>> 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方法。 该方法接受一个参数 - formset中表单的索引。 对于empty_form,索引是None

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

定制一个formset的前缀

在呈现的HTML中,formset在每个字段的名称中包含一个前缀。 默认情况下,前缀是'form',但是可以使用formset的prefix参数进行自定义。

例如,在默认情况下,您可能会看到:

<label for="id_form-0-title">Title:</label>
<input type="text" name="form-0-title" id="id_form-0-title" />

But with ArticleFormset(prefix='article') that becomes:

<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,这非常有用。

在视图和模板中使用formset

在视图中使用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>

但是,通过让formset本身处理管理形式,上面有一个小小的捷径:

<form method="post" action="">
    <table>
        {{ formset }}
    </table>
</form>

上面最后调用了formset类中的as_table方法。

手动呈现can_deletecan_order

如果您手动在模板中渲染字段,则可以使用{{ form.DELETE }}来渲染can_delete / T5> T2>:

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

Similarly, if the formset has the ability to order (can_order=True), it is possible to render it with {{ form.ORDER }}.

在视图中使用多个formset

如果你喜欢,你可以在视图中使用多个formset。 Formset从形式上借用了大部分的行为。 这就是说你可以用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,以便正确呈现和处理。

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