---
title: "フォームセット (Formset)"
version: 4.0
locale: ja
source: https://docs.djangoproject.com/ja/4.0/topics/forms/formsets/
canonical: https://djangodocs.dev/ja/4.0/topics/forms/formsets/
---
# フォームセット (Formset)

#### `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)
```

You now have created a formset class named `ArticleFormSet`.
Instantiating the formset gives you the ability to iterate over the forms
in the formset and display them as you would with a regular form:

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

As you can see it only displayed one empty form. The number of empty forms
that is displayed is controlled by the `extra` parameter. By default,
[`formset_factory()`](/ja/4.0/ref/forms/formsets/#django.forms.formsets.formset_factory) defines one extra form; the
following example will create a formset class to display two blank forms:

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

Iterating over a formset will render the forms in the order they were
created. You can change this order by providing an alternate implementation for
the `__iter__()` method.

フォームセットでは、インデックスをつけて、一致するフォームを返すこともできます。`__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>
```

上の例では、今度は 3 つのフォームが表示されました。初期データとして渡された 1 つと、2 つの追加フォームです。初期データとして、辞書のリストを渡していることにも注意してください。

フォームセットを描画するために `initial` を使う場合、フォームセットの送信を処理するときに同じ `initial` を渡して、どのフォームがユーザによって変更されたかをフォームセットが検出できるようにしてください。例えば、`ArticleFormSet(request.POST, initial=[...])` のようになるでしょう。

> **See also**
>
> [モデルフォームセットを使ってモデルからフォームセットを作る](/ja/4.0/topics/forms/modelforms/#model-formsets).

## フォームの最大表示数を制限する

[`formset_factory()`](/ja/4.0/ref/forms/formsets/#django.forms.formsets.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` の値が初期データ内に存在するオブジェクトの合計より大きい場合、 `extra` を上限として空のフォームがフォームセットに追加されます。 フォームの合計の長さは `max_num` を超えることはできません。例えば、`extra=2` と `max_num=2`、そしてフォームセットが 1 つの `initial` 項目で初期化される場合、この初期項目のフォームと 1 つの空のフォームが表示されます。

初期データ内の項目数が `max_num` を超える場合、`max_num` の値に関わらず全ての初期データのフォームが表示され、追加フォームは 1 つも表示されません。例えば、`extra=3` と `max_num=1`、そしてフォームセットが 2 つの初期項目で初期化される場合、2 つのフォームが初期データとともに表示されます。

`max_num` の値が `None` (デフォルト) だった場合、表示されるフォームの上限は大きな数になります (1000)。この数は、実際には制限がないと見なせるでしょう。

デフォルトでは、`max_num` はいくつのフォームが表示されるかだけに影響し、バリデーションには影響しません。`validate_max=True` が [`formset_factory()`](/ja/4.0/ref/forms/formsets/#django.forms.formsets.formset_factory) に渡される場合は、`max_num` はバリデーションに影響します。[validate\_max](#validate-max) をご覧ください。

## Limiting the maximum number of instantiated forms

> **New in Django 3.2**

The `absolute_max` parameter to [`formset_factory()`](/ja/4.0/ref/forms/formsets/#django.forms.formsets.formset_factory) allows limiting the
number of forms that can be instantiated when supplying `POST` data. This
protects against memory exhaustion attacks using forged `POST` requests:

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

When `absolute_max` is `None`, it defaults to `max_num + 1000`. (If
`max_num` is `None`, it defaults to `2000`).

If `absolute_max` is less than `max_num`, a `ValueError` will be raised.

## フォームセットのバリデーション

フォームセットのバリデーションは、普通の `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` はリストで、 そのエントリーはフォームセット内のフォームと一致します。 バリデーションは、2 つのフォームそれぞれに働いて、2 つ目の項目にエラーメッセージが表示されています。

通常の `Form` を使うときとまったく同じように、フォームセットのフォーム内のそれぞれのフィールドは、ブラウザのバリデーションのための `maxlength` のような HTML 属性を含むことができます。ただし、フォームセットのフォームフィールドは、`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` を理解する

You may have noticed the additional data (`form-TOTAL_FORMS`,
`form-INITIAL_FORMS`) that was required in the formset's data above. This
data is required for the `ManagementForm`. This form is used by the formset
to manage the collection of forms contained in the formset. If you don't
provide this management data, the formset will be invalid:

```
>>> data = {
...     'form-0-title': 'Test',
...     'form-0-pub_date': '',
... }
>>> formset = ArticleFormSet(data)
>>> formset.is_valid()
False
```

これは、表示されているフォームインスタンスの数を追跡するために使用されます。 JavaScriptを使用して新しいフォームを追加する場合は、フォームのカウントフィールドもインクリメントする必要があります。 一方、既存のオブジェクトの削除を許可するためにJavaScriptを使用している場合は、`POST` データに `form-#-DELETE` を含めることで、削除対象のマークが適切に削除されていることを確認する必要があります。 すべてのフォームがそれにかかわらず `POST` データに存在することが期待されます。

ManagementFormは、フォームセット自体の属性として使用できます。 テンプレートでフォームセットをレンダリングするときは、`{{ my_formset.management_form }}` (my\_formsetは適切な名前に置き換えます)をレンダリングすることで、すべての管理データを含めることができます。

> **Note**
>
> As well as the `form-TOTAL_FORMS` and `form-INITIAL_FORMS` fields shown
> in the examples here, the management form also includes
> `form-MIN_NUM_FORMS` and `form-MAX_NUM_FORMS` fields. They are output
> with the rest of the management form, but only for the convenience of
> client-side code. These fields are not required and so are not shown in
> the example `POST` data.

> **Changed in Django 3.2**
>
> `formset.is_valid()` now returns `False` rather than raising an
> exception when the management form is missing or has been tampered with.

### `total_form_count` と `initial_form_count`

`BaseFormSet` には、`ManagementForm`、`total_form_count`、`initial_form_count` と密接に関わる 2 つのメソッドがあります。

`total_form_count` は、対象のフィールドセット内のフォームの合計数を返します。`initial_form_count` は、記入前のフォームセット内のフォームの数を返し、またいくつのフォームが必須なのかを決めるためにも使われます。通常、これらのメソッドをオーバーライドする必要はありませんが、もし必要な場合はメソッドの動作を理解してからオーバーライドしてください。

### `empty_form`

`BaseFormSet` には追加の属性 `empty_form` があり、`__prefix__` というプレフィックスとともにフォームのインスタンスを返します。これにより、JavaScript で動的にフォームを操作することが容易となります。

### `error_messages`

> **New in Django 3.2**

The `error_messages` argument lets you override the default messages that the
formset will raise. Pass in a dictionary with keys matching the error messages
you want to override. For example, here is the default error message when the
management form is missing:

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

And here is a custom error message:

```
>>> formset = ArticleFormSet({}, error_messages={'missing_management_form': 'Sorry, something went wrong.'})
>>> formset.is_valid()
False
>>> formset.non_form_errors()
['Sorry, something went wrong.']
```

### カスタムフォームセットのバリデーション

フォームセットには、`Form` クラスと同じような `clean` メソッドがあります。フォームセットのレベルで検証するためのバリデーションは、ここに記述します:

```
>>> 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()` メソッドを使います。

Non-form errors will be rendered with an additional class of `nonform` to
help distinguish them from form-specific errors. For example,
`{{ formset.non_form_errors }}` would look like:

```html+django
<ul class="errorlist nonform">
    <li>Articles in a set must have distinct titles.</li>
</ul>
```

> **Changed in Django 4.0**
>
> The additional `nonform` class was added.

## フォームセット内のフォームの数を検証する

送信されたフォームの最小および最大数を検証するために、Django にはいくつかの方法が用意されています。フォームの数のバリデーションをさらにカスタマイズする必要があるときは、カスタムのフォームセットバリデーションを使用する必要があります。

### `validate_max`

If `validate_max=True` is passed to
[`formset_factory()`](/ja/4.0/ref/forms/formsets/#django.forms.formsets.formset_factory), validation will also check
that the number of forms in the data set, minus those marked for
deletion, is less than or equal to `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` validates against `max_num` strictly even if
`max_num` was exceeded because the amount of initial data supplied was
excessive.

> **Note**
>
> Regardless of `validate_max`, if the number of forms in a data set
> exceeds `absolute_max`, then the form will fail to validate as if
> `validate_max` were set, and additionally only the first `absolute_max`
> forms will be validated. The remainder will be truncated entirely. This is
> to protect against memory exhaustion attacks using forged POST requests.
> See [Limiting the maximum number of instantiated forms](#formsets-absolute-max).

### `validate_min`

If `validate_min=True` is passed to
[`formset_factory()`](/ja/4.0/ref/forms/formsets/#django.forms.formsets.formset_factory), validation will also check
that the number of forms in the data set, minus those marked for
deletion, is greater than or equal to `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.']
```

> **Note**
>
> Regardless of `validate_min`, if a formset contains no data, then
> `extra + min_num` empty forms will be displayed.

## Dealing with ordering and deletion of forms

[`formset_factory()`](/ja/4.0/ref/forms/formsets/#django.forms.formsets.formset_factory) には、フォームセット内のフォームの順序およびフォームセットからのフォームの削除に役立つ 2 つのオプション引数があります。

### `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`](#django.forms.formsets.BaseFormSet) also provides an
[`ordering_widget`](#django.forms.formsets.BaseFormSet.ordering_widget) attribute and
[`get_ordering_widget()`](#django.forms.formsets.BaseFormSet.get_ordering_widget) method that
control the widget used with
[`can_order`](#django.forms.formsets.BaseFormSet.can_order).

#### `ordering_widget`

#### `BaseFormSet.ordering_widget`

Default: [`NumberInput`](/ja/4.0/ref/forms/widgets/#django.forms.NumberInput)

Set `ordering_widget` to specify the widget class to be used with
`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()`

Override `get_ordering_widget()` if you need to provide a widget instance for
use with `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>
```

Similar to `can_order` this adds a new field to each form named `DELETE`
and is a `forms.BooleanField`. When data comes through marking any of the
delete fields you can access them with `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'}]
```

If you are using a [`ModelFormSet`](/ja/4.0/topics/forms/modelforms/#django.forms.models.BaseModelFormSet),
model instances for deleted forms will be deleted when you call
`formset.save()`.

If you call `formset.save(commit=False)`, objects will not be deleted
automatically.  You'll need to call `delete()` on each of the
[`formset.deleted_objects`](/ja/4.0/topics/forms/modelforms/#django.forms.models.BaseModelFormSet.deleted_objects) to actually delete
them:

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

[`BaseFormSet`](#django.forms.formsets.BaseFormSet) also provides a
[`deletion_widget`](#django.forms.formsets.BaseFormSet.deletion_widget) attribute and
[`get_deletion_widget()`](#django.forms.formsets.BaseFormSet.get_deletion_widget) method that
control the widget used with
[`can_delete`](#django.forms.formsets.BaseFormSet.can_delete).

#### `deletion_widget`

> **New in Django 4.0**

#### `BaseFormSet.deletion_widget`

Default: [`CheckboxInput`](/ja/4.0/ref/forms/widgets/#django.forms.CheckboxInput)

Set `deletion_widget` to specify the widget class to be used with
`can_delete`:

```
>>> from django.forms import BaseFormSet, formset_factory
>>> from myapp.forms import ArticleForm
>>> class BaseArticleFormSet(BaseFormSet):
...     deletion_widget = HiddenInput

>>> ArticleFormSet = formset_factory(ArticleForm, formset=BaseArticleFormSet, can_delete=True)
```

#### `get_deletion_widget`

> **New in Django 4.0**

#### `BaseFormSet.get_deletion_widget()`

Override `get_deletion_widget()` if you need to provide a widget instance for
use with `can_delete`:

```
>>> from django.forms import BaseFormSet, formset_factory
>>> from myapp.forms import ArticleForm
>>> class BaseArticleFormSet(BaseFormSet):
...     def get_deletion_widget(self):
...         return HiddenInput(attrs={'class': 'deletion'})

>>> ArticleFormSet = formset_factory(ArticleForm, formset=BaseArticleFormSet, can_delete=True)
```

### `can_delete_extra`

> **New in Django 3.2**

#### `BaseFormSet.can_delete_extra`

デフォルト値: `True`

While setting `can_delete=True`, specifying `can_delete_extra=False` will
remove the option to delete extra forms.

## Adding additional fields to a formset

If you need to add additional fields to the formset this can be easily
accomplished. The formset base class provides an `add_fields` method. You
can override this method to add your own fields or even redefine the default
fields/attributes of the order and deletion 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>
```

## Passing custom parameters to formset forms

Sometimes your form class takes custom parameters, like `MyArticleForm`.
You can pass this parameter when instantiating the 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})
```

The `form_kwargs` may also depend on the specific form instance. The formset
base class provides a `get_form_kwargs` method. The method takes a single
argument - the index of the form in the formset. The index is `None` for the
[empty\_form](#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
```

## Customizing a formset's prefix

In the rendered HTML, formsets include a prefix on each field's name. By
default, the prefix is `'form'`, but it can be customized using the formset's
`prefix` argument.

For example, in the default case, you might see:

```html
<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:

```html
<label for="id_article-0-title">Title:</label>
<input type="text" name="article-0-title" id="id_article-0-title">
```

This is useful if you want to [use more than one formset in a view](#multiple-formsets-in-view).

## Using a formset in views and templates

Formsets have five attributes and five methods associated with rendering.

#### `BaseFormSet.renderer`

> **New in Django 4.0**

Specifies the [renderer](/ja/4.0/ref/forms/renderers/) to use for the
formset. Defaults to the renderer specified by the [`FORM_RENDERER`](/ja/4.0/ref/settings/#std-setting-FORM_RENDERER)
setting.

#### `BaseFormSet.template_name`

> **New in Django 4.0**

The name of the template used when calling `__str__` or [`render()`](#django.forms.formsets.BaseFormSet.render).
This template renders the formset's management form and then each form in
the formset as per the template defined by the form's
[`template_name`](/ja/4.0/ref/forms/api/#django.forms.Form.template_name). This is a proxy of `as_table`
by default.

#### `BaseFormSet.template_name_p`

> **New in Django 4.0**

The name of the template used when calling [`as_p()`](#django.forms.formsets.BaseFormSet.as_p). By default this
is `'django/forms/formsets/p.html'`. This template renders the formset's
management form and then each form in the formset as per the form's
[`as_p()`](/ja/4.0/ref/forms/api/#django.forms.Form.as_p) method.

#### `BaseFormSet.template_name_table`

> **New in Django 4.0**

The name of the template used when calling [`as_table()`](#django.forms.formsets.BaseFormSet.as_table). By default
this is `'django/forms/formsets/table.html'`. This template renders the
formset's management form and then each form in the formset as per the
form's [`as_table()`](/ja/4.0/ref/forms/api/#django.forms.Form.as_table) method.

#### `BaseFormSet.template_name_ul`

> **New in Django 4.0**

The name of the template used when calling [`as_ul()`](#django.forms.formsets.BaseFormSet.as_ul). By default this
is `'django/forms/formsets/ul.html'`. This template renders the formset's
management form and then each form in the formset as per the form's
[`as_ul()`](/ja/4.0/ref/forms/api/#django.forms.Form.as_ul) method.

#### `BaseFormSet.get_context()`

> **New in Django 4.0**

Returns the context for rendering a formset in a template.

The available context is:

- `formset` : The instance of the formset.

#### `BaseFormSet.render(template_name=None, context=None, renderer=None)`

> **New in Django 4.0**

The render method is called by `__str__` as well as the [`as_p()`](#django.forms.formsets.BaseFormSet.as_p),
[`as_ul()`](#django.forms.formsets.BaseFormSet.as_ul), and [`as_table()`](#django.forms.formsets.BaseFormSet.as_table) methods. All arguments are optional
and will default to:

- `template_name`: [`template_name`](#django.forms.formsets.BaseFormSet.template_name)
- `context`: Value returned by [`get_context()`](#django.forms.formsets.BaseFormSet.get_context)
- `renderer`: Value returned by [`renderer`](#django.forms.formsets.BaseFormSet.renderer)

#### `BaseFormSet.as_p()`

Renders the formset with the [`template_name_p`](#django.forms.formsets.BaseFormSet.template_name_p) template.

#### `BaseFormSet.as_table()`

Renders the formset with the [`template_name_table`](#django.forms.formsets.BaseFormSet.template_name_table) template.

#### `BaseFormSet.as_ul()`

Renders the formset with the [`template_name_ul`](#django.forms.formsets.BaseFormSet.template_name_ul) template.

Using a formset inside a view is not very different from using a regular
`Form` class. The only thing you will want to be aware of is making sure to
use the management form inside the template. Let's look at a sample view:

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

The `manage_articles.html` template might look like this:

```html+django
<form method="post">
    {{ formset.management_form }}
    <table>
        {% for form in formset %}
        {{ form }}
        {% endfor %}
    </table>
</form>
```

However there's a slight shortcut for the above by letting the formset itself
deal with the management form:

```html+django
<form method="post">
    <table>
        {{ formset }}
    </table>
</form>
```

The above ends up calling the [`BaseFormSet.render()`](#django.forms.formsets.BaseFormSet.render) method on the formset
class. This renders the formset using the template specified by the
[`template_name`](#django.forms.formsets.BaseFormSet.template_name) attribute. Similar to forms, by default the
formset will be rendered `as_table`, with other helper methods of `as_p`
and `as_ul` being available. The rendering of the formset can be customized
by specifying the `template_name` attribute, or more generally by
[overriding the default template](/ja/4.0/ref/forms/renderers/#overriding-built-in-formset-templates).

> **Changed in Django 4.0**
>
> Rendering of formsets was moved to the template engine.

### Manually rendered `can_delete` and `can_order`

If you manually render fields in the template, you can render
`can_delete` parameter with `{{ form.DELETE }}`:

```html+django
<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>
```

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

### Using more than one formset in a view

You are able to use more than one formset in a view if you like. Formsets
borrow much of its behavior from forms. With that said you are able to use
`prefix` to prefix formset form field names with a given value to allow
more than one formset to be sent to a view without name clashing. Let's take
a look at how this might be accomplished:

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

You would then render the formsets as normal. It is important to point out
that you need to pass `prefix` on both the POST and non-POST cases so that
it is rendered and processed correctly.

Each formset's [prefix](#formset-prefix) replaces the default `form`
prefix that's added to each field's `name` and `id` HTML attributes.
