# Django 4.1.8.dev20230308103347 ドキュメント

# フォームセット (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()` 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=[...])` のようになるでしょう。

参考

モデルフォームセットを使ってモデルからフォームセットを作る.

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

`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()` に渡される場合は、`max_num` はバリデーションに影響します。validate_max をご覧ください。

## Limiting the maximum number of instantiated forms

The `absolute_max` parameter to `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は適切な名前に置き換えます)をレンダリングすることで、すべての管理データを含めることができます。

注釈

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.

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

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. Error message keys include `'too_few_forms'`, `'too_many_forms'`, and `'missing_management_form'`. The `'too_few_forms'` and `'too_many_forms'` error messages may contain `%(num)d`, which will be replaced with `min_num` and `max_num`, respectively.

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

Changed in Django 4.1:

The `'too_few_forms'` and `'too_many_forms'` keys were added.

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

フォームセットには、`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:

    <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()`, 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.

The error message can be customized by passing the `'too_many_forms'` message to the error_messages argument.

注釈

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.

### `validate_min`

If `validate_min=True` is passed to `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.']

The error message can be customized by passing the `'too_few_forms'` message to the error_messages argument.

注釈

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()` には、フォームセット内のフォームの順序およびフォームセットからのフォームの削除に役立つ 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` also provides an `ordering_widget` attribute and `get_ordering_widget()` method that control the widget used with `can_order`.

#### `ordering_widget`

`BaseFormSet.``ordering_widget`

Default: `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`, 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` 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` also provides a `deletion_widget` attribute and `get_deletion_widget()` method that control the widget used with `can_delete`.

#### `deletion_widget`

New in Django 4.0.

`BaseFormSet.``deletion_widget`

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

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

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

    >>> ArticleFormSet = formset_factory(MyArticleForm, formset=BaseArticleFormSet)
    >>> formset = ArticleFormSet()

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

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

This is useful if you want to use more than one formset in a view.

## Using a formset in views and templates

Formsets have the following attributes and methods associated with rendering:

`BaseFormSet.``renderer`
  New in Django 4.0.

  Specifies the renderer to use for the formset. Defaults to the renderer specified by the `FORM_RENDERER` setting.

`BaseFormSet.``template_name`
  New in Django 4.0.

  The name of the template rendered if the formset is cast into a string, e.g. via `print(formset)` or in a template via `{{ formset }}`.

  By default, a property returning the value of the renderer's `formset_template_name`. You may set it as a string template name in order to override that for a particular formset class.

  This template will be used to render the formset's management form, and then each form in the formset as per the template defined by the form's `template_name`.

  Changed in Django 4.1:

  In older versions `template_name` defaulted to the string value `'django/forms/formset/default.html'`.

`BaseFormSet.``template_name_div`
  New in Django 4.1.

  The name of the template used when calling `as_div()`. By default this is `"django/forms/formsets/div.html"`. This template renders the formset's management form and then each form in the formset as per the form's `as_div()` method.

`BaseFormSet.``template_name_p`
  New in Django 4.0.

  The name of the template used when calling `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()` method.

`BaseFormSet.``template_name_table`
  New in Django 4.0.

  The name of the template used when calling `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()` method.

`BaseFormSet.``template_name_ul`
  New in Django 4.0.

  The name of the template used when calling `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()` 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_div()`, `as_p()`, `as_ul()`, and `as_table()` methods. All arguments are optional and will default to:

  - `template_name`: `template_name`
  - `context`: Value returned by `get_context()`
  - `renderer`: Value returned by `renderer`

`BaseFormSet.``as_div`()
  New in Django 4.1.

  Renders the formset with the `template_name_div` template.

`BaseFormSet.``as_p`()

  Renders the formset with the `template_name_p` template.

`BaseFormSet.``as_table`()

  Renders the formset with the `template_name_table` template.

`BaseFormSet.``as_ul`()

  Renders the formset with the `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:

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

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

The above ends up calling the `BaseFormSet.render()` method on the formset class. This renders the formset using the template specified by the `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.

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

    <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 replaces the default `form` prefix that's added to each field's `name` and `id` HTML attributes.