# Django 5.2.8 documentation

# Formsatser

class BaseFormSet

En formset är ett abstraktionslager som gör det möjligt att arbeta med flera formulär på samma sida. Det kan bäst jämföras med ett datagalleri. Låt oss säga att du har följande formulär:

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

Du kanske vill tillåta användaren att skapa flera artiklar på en gång. För att skapa en formuläruppsättning av en `ArticleForm` gör du så här:

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

Du har nu skapat en formulärklass med namnet `ArticleFormSet`. Genom att instansiera formuläruppsättningen kan du iterera över formulären i formuläruppsättningen och visa dem som du skulle göra med ett vanligt formulär:

    >>> formset = ArticleFormSet()
    >>> for form in formset:
    ...     print(form)
    ...
    <div><label for="id_form-0-title">Title:</label><input type="text" name="form-0-title" id="id_form-0-title"></div>
    <div><label for="id_form-0-pub_date">Pub date:</label><input type="text" name="form-0-pub_date" id="id_form-0-pub_date"></div>

Som du kan se visade den bara ett tomt formulär. Antalet tomma formulär som visas styrs av parametern `extra`. Som standard definierar `formset_factory()` ett extra formulär; följande exempel kommer att skapa en formset-klass för att visa två tomma formulär:

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

Formsets can be iterated and indexed, accessing forms in the order they were created. You can reorder the forms by overriding the default `iteration` and `indexing` behavior if needed.

## Använda initiala data med en formulärset

Initiala data är det som styr den huvudsakliga användbarheten för en formuläruppsättning. Som visas ovan kan du definiera antalet extra formulär. Det innebär att du talar om för formuläret hur många ytterligare formulär som ska visas utöver det antal formulär som genereras utifrån de ursprungliga uppgifterna. Låt oss ta en titt på ett exempel:

    >>> 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)
    ...
    <div><label for="id_form-0-title">Title:</label><input type="text" name="form-0-title" value="Django is now open source" id="id_form-0-title"></div>
    <div><label for="id_form-0-pub_date">Pub date:</label><input type="text" name="form-0-pub_date" value="2023-02-11" id="id_form-0-pub_date"></div>
    <div><label for="id_form-1-title">Title:</label><input type="text" name="form-1-title" id="id_form-1-title"></div>
    <div><label for="id_form-1-pub_date">Pub date:</label><input type="text" name="form-1-pub_date" id="id_form-1-pub_date"></div>
    <div><label for="id_form-2-title">Title:</label><input type="text" name="form-2-title" id="id_form-2-title"></div>
    <div><label for="id_form-2-pub_date">Pub date:</label><input type="text" name="form-2-pub_date" id="id_form-2-pub_date"></div>

Det finns nu totalt tre formulär som visas ovan. Ett för de initiala data som skickades in och två extra formulär. Observera också att vi skickar in en lista med ordböcker som startdata.

Om du använder en `initial` för att visa ett formulär bör du skicka samma `initial` när du behandlar formulärets inlämning så att formuläret kan upptäcka vilka formulär som har ändrats av användaren. Till exempel kan du ha något liknande: `ArticleFormSet(request.POST, initial=[...])`.

Se även

Skapande av formulär från modeller med modellformulär.

## Begränsa det maximala antalet formulär

Parametern `max_num` till `formset_factory()` ger dig möjlighet att begränsa antalet formulär som formuläret ska visa:

    >>> 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)
    ...
    <div><label for="id_form-0-title">Title:</label><input type="text" name="form-0-title" id="id_form-0-title"></div>
    <div><label for="id_form-0-pub_date">Pub date:</label><input type="text" name="form-0-pub_date" id="id_form-0-pub_date"></div>

Om värdet för `max_num` är större än antalet befintliga objekt i de ursprungliga uppgifterna, kommer upp till `extra` ytterligare blanketter att läggas till i formuläruppsättningen, så länge det totala antalet blanketter inte överstiger `max_num`. Om t.ex. `extra=2` och `max_num=2` och formuläruppsättningen initieras med ett `initial` objekt, kommer ett formulär för det första objektet och ett tomt formulär att visas.

Om antalet objekt i startdata överstiger `max_num` visas alla formulär med startdata oavsett värdet på `max_num` och inga extra formulär visas. Om t.ex. `extra=3` och `max_num=1` och formuläruppsättningen initieras med två initiala objekt, visas två formulär med de initiala uppgifterna.

Ett värde på `max_num` på `None` (standard) sätter en hög gräns för antalet formulär som visas (1000). I praktiken är detta likvärdigt med ingen gräns.

By default, `max_num` only affects how many forms are displayed and does not affect validation. If `validate_max=True` is passed to the `formset_factory()`, then `max_num` will affect validation. See validera_max.

## Begränsning av det maximala antalet instansierade formulär

Parametern `absolute_max` till `formset_factory()` gör det möjligt att begränsa antalet formulär som kan instansieras när `POST`-data levereras. Detta skyddar mot minnesutmattningsattacker med hjälp av förfalskade `POST`-förfrågningar:

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

När `absolute_max` är `None`, är standardvärdet `max_num + 1000`. (Om `max_num` är `None`, är standardvärdet `2000`).

Om `absolute_max` är mindre än `max_num`, kommer ett `ValueError` att uppstå.

## Validering av formulär

Validering med en formset är nästan identisk med en vanlig `Form`. Det finns en `is_valid`-metod på formuläret för att tillhandahålla ett bekvämt sätt att validera alla formulär i formuläret:

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

Vi har inte skickat in några data till formuläret, vilket resulterar i ett giltigt formulär. Formuläret är tillräckligt smart för att ignorera extra formulär som inte har ändrats. Om vi tillhandahåller en ogiltig artikel:

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

Som vi kan se är `formset.errors` en lista vars poster motsvarar formulären i formuläruppsättningen. Validering utfördes för vart och ett av de två formulären, och det förväntade felmeddelandet visas för det andra objektet.

Precis som när du använder ett vanligt `Form` kan varje fält i ett formulär innehålla HTML-attribut som `maxlength` för webbläsarvalidering. Formulärfält i formuläruppsättningar kommer dock inte att innehålla attributet `required` eftersom den valideringen kan vara felaktig när formulär läggs till och tas bort.

BaseFormSet.total_error_count()

För att kontrollera hur många fel som finns i formuläret kan vi använda metoden `total_error_count`:

    >>> # Using the previous example
    >>> formset.errors
    [{}, {'pub_date': ['This field is required.']}]
    >>> len(formset.errors)
    2
    >>> formset.total_error_count()
    1

Vi kan också kontrollera om uppgifterna i formuläret skiljer sig från de ursprungliga uppgifterna (dvs. om formuläret skickades utan några uppgifter):

    >>> data = {
    ...     "form-TOTAL_FORMS": "1",
    ...     "form-INITIAL_FORMS": "0",
    ...     "form-0-title": "",
    ...     "form-0-pub_date": "",
    ... }
    >>> formset = ArticleFormSet(data)
    >>> formset.has_changed()
    False

### Förståelse för ”Managementformuläret

Du kanske har lagt märke till de ytterligare data (`form-TOTAL_FORMS`, `form-INITIAL_FORMS`) som krävdes i formulärets data ovan. Dessa data krävs för `ManagementForm`. Detta formulär används av formuläruppsättningen för att hantera den samling formulär som finns i formuläruppsättningen. Om du inte tillhandahåller dessa hanteringsdata kommer formuläret att vara ogiltigt:

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

Det används för att hålla reda på hur många formulärinstanser som visas. Om du lägger till nya formulär via JavaScript bör du inkrementera count-fälten även i detta formulär. Om du å andra sidan använder JavaScript för att tillåta borttagning av befintliga objekt, måste du se till att de som tas bort är korrekt markerade för borttagning genom att inkludera `form-#-DELETE` i `POST`-data. Det förväntas att alla formulär finns i `POST`-datan oavsett.

Hanteringsformuläret är tillgängligt som ett attribut för själva formuläret. När du renderar en formset i en mall kan du inkludera alla hanteringsdata genom att rendera `{{ my_formset.management_form }}` (med namnet på din formset som ersättning).

Observera

Förutom fälten `form-TOTAL_FORMS` och `form-INITIAL_FORMS` som visas i exemplen här, innehåller hanteringsformuläret också fälten `form-MIN_NUM_FORMS` och `form-MAX_NUM_FORMS`. De matas ut tillsammans med resten av hanteringsformuläret, men endast för att underlätta för koden på klientsidan. Dessa fält är inte obligatoriska och visas därför inte i exemplet med `POST`-data.

### `total_form_count` och `initial_form_count`

`BaseFormSet` har ett par metoder som är nära relaterade till `ManagementForm`, `total_form_count` och `initial_form_count`.

`total_form_count` returnerar det totala antalet formulär i denna formuläruppsättning. `initial_form_count` returnerar antalet formulär i formuläruppsättningen som var förifyllda, och används också för att avgöra hur många formulär som krävs. Du kommer förmodligen aldrig att behöva åsidosätta någon av dessa metoder, så se till att du förstår vad de gör innan du gör det.

### `tomt_formulär`

`BaseFormSet` tillhandahåller ett ytterligare attribut `empty_form` som returnerar en formulärinstans med prefixet `__prefix__` för enklare användning i dynamiska formulär med JavaScript.

### `Felmeddelanden`

Med argumentet `error_messages` kan du åsidosätta de standardmeddelanden som formuläret kommer att ge upphov till. Skicka in en ordbok med nycklar som matchar de felmeddelanden som du vill åsidosätta. Felmeddelanden är bland annat `'too_few_forms'`, `'too_many_forms'` och `'missing_management_form'`. Felmeddelandena `'too_few_forms'` och `'too_many_forms'` kan innehålla `%(num)d`, som kommer att ersättas med `min_num` respektive `max_num`.

Här är t.ex. standardfelmeddelandet när förvaltningsformuläret saknas:

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

Och här är ett anpassat felmeddelande:

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

### Anpassad validering av formulär

Ett formulär har en `clean`-metod som liknar den som finns i klassen `Form`. Det är här du definierar din egen validering som fungerar på formulärnivå:

    >>> 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 = set()
    ...         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.add(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.']

Metoden `clean` i formuläret anropas efter att alla `Form.clean`-metoder har anropats. Felen kommer att hittas med hjälp av metoden `non_form_errors()` på formuläret.

Icke-formulärfel återges med en extra klass `nonform` för att skilja dem från formulärspecifika fel. Till exempel skulle `{{ formset.non_form_errors }}` se ut så här:

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

## Validering av antalet formulär i en formuläruppsättning

Django tillhandahåller ett par sätt att validera det lägsta eller högsta antalet inlämnade formulär. Applikationer som behöver mer anpassningsbar validering av antalet formulär bör använda anpassad validering av formuläruppsättningar.

### `validera_max`

Om `validate_max=True` skickas till `formset_factory()`, kommer valideringen också att kontrollera att antalet formulär i datauppsättningen, minus de som är markerade för borttagning, är mindre än eller lika med `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` validerar mot `max_num` strikt även om `max_num` överskreds på grund av att mängden initiala data som levererades var för stor.

Felmeddelandet kan anpassas genom att skicka meddelandet `'too_many_forms'` till argumentet Felmeddelanden.

Observera

Oavsett `validate_max`, om antalet formulär i en datauppsättning överstiger `absolute_max`, kommer formuläret inte att valideras som om `validate_max` hade angetts, och dessutom kommer endast de första `absolute_max` formulären att valideras. Resten kommer att avkortas helt och hållet. Detta är för att skydda mot minnesutmattningsattacker med hjälp av förfalskade POST-begäranden. Se formuläruppsättningar-absolute-max.

### `validera_min`

Om `validate_min=True` skickas till `formset_factory()`, kommer valideringen också att kontrollera att antalet formulär i datauppsättningen, minus de som är markerade för borttagning, är större än eller lika med `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.']

Felmeddelandet kan anpassas genom att skicka meddelandet `'too_few_forms'` till argumentet Felmeddelanden.

Observera

Oavsett `validate_min`, om en formuläruppsättning inte innehåller några data, kommer `extra + min_num` tomma formulär att visas.

## Hantering av beställning och borttagning av formulär

`formset_factory()` tillhandahåller två valfria parametrar `can_order` och `can_delete` för att hjälpa till med ordning av formulär i formulär och borttagning av formulär från ett formulär.

### `kan beställa`

BaseFormSet.can_order

Standard: `False`

Låter dig skapa ett formulär med möjlighet att beställa:

    >>> 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)
    ...
    <div><label for="id_form-0-title">Title:</label><input type="text" name="form-0-title" value="Article #1" id="id_form-0-title"></div>
    <div><label for="id_form-0-pub_date">Pub date:</label><input type="text" name="form-0-pub_date" value="2008-05-10" id="id_form-0-pub_date"></div>
    <div><label for="id_form-0-ORDER">Order:</label><input type="number" name="form-0-ORDER" value="1" id="id_form-0-ORDER"></div>
    <div><label for="id_form-1-title">Title:</label><input type="text" name="form-1-title" value="Article #2" id="id_form-1-title"></div>
    <div><label for="id_form-1-pub_date">Pub date:</label><input type="text" name="form-1-pub_date" value="2008-05-11" id="id_form-1-pub_date"></div>
    <div><label for="id_form-1-ORDER">Order:</label><input type="number" name="form-1-ORDER" value="2" id="id_form-1-ORDER"></div>
    <div><label for="id_form-2-title">Title:</label><input type="text" name="form-2-title" id="id_form-2-title"></div>
    <div><label for="id_form-2-pub_date">Pub date:</label><input type="text" name="form-2-pub_date" id="id_form-2-pub_date"></div>
    <div><label for="id_form-2-ORDER">Order:</label><input type="number" name="form-2-ORDER" id="id_form-2-ORDER"></div>

Detta lägger till ett extra fält i varje formulär. Det nya fältet heter `ORDER` och är ett `forms.IntegerField`. För de formulär som kom från de ursprungliga uppgifterna tilldelades de automatiskt ett numeriskt värde. Låt oss titta på vad som kommer att hända när användaren ändrar dessa värden:

    >>> 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)},
    ...     ],
    ... )
    >>> for form in formset.ordered_forms:
    ...     print(form.cleaned_data)
    ...
    {'title': 'Article #3', 'pub_date': datetime.date(2008, 5, 1), 'ORDER': 0}
    {'title': 'Article #2', 'pub_date': datetime.date(2008, 5, 11), 'ORDER': 1}
    {'title': 'Article #1', 'pub_date': datetime.date(2008, 5, 10), 'ORDER': 2}

`BaseFormSet` tillhandahåller också attributet `ordering_widget` och metoden `get_ordering_widget()` som styr widgeten som används med `can_order`.

#### `beställning_widget`

BaseFormSet.ordering_widget

Standard: `NumberInput`

Ange `ordering_widget` för att specificera widgetklassen som ska användas med `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
    ... )

#### hämta beställningswidget

BaseFormSet.get_ordering_widget()

Åsidosätt `get_ordering_widget()` om du behöver tillhandahålla en widgetinstans för användning med `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
    ... )

### `kan radera`

BaseFormSet.can_delete

Standard: `False`

Låter dig skapa en formuläruppsättning med möjlighet att välja formulär för borttagning:

    >>> 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)
    ...
    <div><label for="id_form-0-title">Title:</label><input type="text" name="form-0-title" value="Article #1" id="id_form-0-title"></div>
    <div><label for="id_form-0-pub_date">Pub date:</label><input type="text" name="form-0-pub_date" value="2008-05-10" id="id_form-0-pub_date"></div>
    <div><label for="id_form-0-DELETE">Delete:</label><input type="checkbox" name="form-0-DELETE" id="id_form-0-DELETE"></div>
    <div><label for="id_form-1-title">Title:</label><input type="text" name="form-1-title" value="Article #2" id="id_form-1-title"></div>
    <div><label for="id_form-1-pub_date">Pub date:</label><input type="text" name="form-1-pub_date" value="2008-05-11" id="id_form-1-pub_date"></div>
    <div><label for="id_form-1-DELETE">Delete:</label><input type="checkbox" name="form-1-DELETE" id="id_form-1-DELETE"></div>
    <div><label for="id_form-2-title">Title:</label><input type="text" name="form-2-title" id="id_form-2-title"></div>
    <div><label for="id_form-2-pub_date">Pub date:</label><input type="text" name="form-2-pub_date" id="id_form-2-pub_date"></div>
    <div><label for="id_form-2-DELETE">Delete:</label><input type="checkbox" name="form-2-DELETE" id="id_form-2-DELETE"></div>

I likhet med `can_order` lägger detta till ett nytt fält till varje formulär som heter `DELETE` och är ett `forms.BooleanField`. När data kommer genom att markera något av delete-fälten kan du komma åt dem med `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]
    [{'title': 'Article #1', 'pub_date': datetime.date(2008, 5, 10), 'DELETE': True}]

Om du använder en `ModelFormSet`, kommer modellinstanser för borttagna formulär att raderas när du anropar `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()
    ...

Om du å andra sidan använder ett vanligt `FormSet` är det upp till dig att hantera `formset.deleted_forms`, kanske i ditt formsets `save()`-metod, eftersom det inte finns någon allmän uppfattning om vad det innebär att ta bort ett formulär.

`BaseFormSet` tillhandahåller också attributet `deletion_widget` och metoden `get_deletion_widget()` som styr widgeten som används med `can_delete`.

#### `deletion_widget`

BaseFormSet.deletion_widget

Standard: `CheckboxInput`

Ange `deletion_widget` för att specificera widgetklassen som ska användas med `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`

BaseFormSet.get_deletion_widget()

Åsidosätt `get_deletion_widget()` om du behöver tillhandahålla en widgetinstans för användning med `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
    ... )

### `kan_radera_extra`

BaseFormSet.can_delete_extra

Standard: `True`

När du anger `can_delete=True` och specificerar `can_delete_extra=False` tas möjligheten att radera extra formulär bort.

## Lägga till ytterligare fält i en formuläruppsättning

Om du behöver lägga till ytterligare fält i formuläret kan detta enkelt göras. Basklassen för formuläret innehåller metoden `add_fields`. Du kan åsidosätta denna metod för att lägga till dina egna fält eller till och med omdefiniera standardfälten/attributen för order- och borttagningsfälten:

    >>> 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)
    ...
    <div><label for="id_form-0-title">Title:</label><input type="text" name="form-0-title" id="id_form-0-title"></div>
    <div><label for="id_form-0-pub_date">Pub date:</label><input type="text" name="form-0-pub_date" id="id_form-0-pub_date"></div>
    <div><label for="id_form-0-my_field">My field:</label><input type="text" name="form-0-my_field" id="id_form-0-my_field"></div>

## Överföra anpassade parametrar till formulär

Ibland tar din formulärklass anpassade parametrar, till exempel `MyArticleForm`. Du kan skicka den här parametern när du instansierar formuläret:

    >>> 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` kan också bero på den specifika formulärinstansen. Basklassen formset tillhandahåller metoden `get_form_kwargs`. Metoden tar ett enda argument - indexet för formuläret i formuläruppsättningen. Indexet är `None` för tomt_formulär:

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

## Anpassa prefixet för ett formulär

I den renderade HTML-filen inkluderar formulär ett prefix på varje fältnamn. Som standard är prefixet `'form'`, men det kan anpassas med hjälp av formulärets `prefix`-argument.

I standardfallet kan du till exempel se:

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

Men med `ArticleFormset(prefix='article')` blir det så:

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

Detta är användbart om du vill använda mer än ett formulär i en vy.

## Använda en formulärsats i vyer och mallar

Formset har följande attribut och metoder som är associerade med rendering:

BaseFormSet.renderer

  Anger vilken renderer som ska användas för formuläret. Standard är den rendering som anges av inställningen `FORM_RENDERER`.

BaseFormSet.template_name

  Namnet på den mall som återges om formuläret omvandlas till en sträng, t.ex. via `print(formset)` eller i en mall via `{{ formset }}`.

  Som standard en egenskap som returnerar värdet av renderingsprogrammets `formset_template_name`. Du kan ange det som ett mallnamn i form av en sträng för att åsidosätta det för en viss formulärklass.

  Denna mall kommer att användas för att rendera formuläruppsättningens hanteringsformulär och sedan varje formulär i formuläruppsättningen enligt den mall som definieras av formulärets `template_name`.

BaseFormSet.template_name_div

  Namnet på den mall som används vid anrop av `as_div()`. Som standard är detta `"django/forms/formsets/div.html"`. Den här mallen renderar formuläruppsättningens hanteringsformulär och sedan varje formulär i formuläruppsättningen enligt formulärets `as_div()`-metod.

BaseFormSet.template_name_p

  Namnet på den mall som används vid anrop av `as_p()`. Som standard är detta `"django/forms/formsets/p.html"`. Den här mallen renderar formuläruppsättningens hanteringsformulär och sedan varje formulär i formuläruppsättningen enligt formulärets `as_p()`-metod.

BaseFormSet.template_name_table

  Namnet på den mall som används vid anrop av `as_table()`. Som standard är detta `"django/forms/formsets/table.html"`. Den här mallen renderar formuläruppsättningens hanteringsformulär och sedan varje formulär i formuläruppsättningen enligt formulärets `as_table()`-metod.

BaseFormSet.template_name_ul

  Namnet på den mall som används vid anrop av `as_ul()`. Som standard är detta `"django/forms/formsets/ul.html"`. Den här mallen renderar formuläruppsättningens hanteringsformulär och sedan varje formulär i formuläruppsättningen enligt formulärets `as_ul()`-metod.

BaseFormSet.get_context()

  Returnerar kontexten för rendering av en formulärsats i en mall.

  Den tillgängliga kontexten är:

  - `formset` : Instansen av formuläret.

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

  Renderingsmetoden anropas av `__str__` samt metoderna `as_div()`, `as_p()`, `as_ul()` och `as_table()`. Alla argument är valfria och kommer som standard att vara:

  - `template_name`: `template_name`

  - `context`: Värde som returneras av `get_context()`

  - `renderer`: Värde som returneras av `renderer`

BaseFormSet.as_div()

  Renderar formuläret med mallen `template_name_div`.

BaseFormSet.as_p()

  Renderar formuläret med mallen `template_name_p`.

BaseFormSet.as_table()

  Renderar formuläret med mallen `template_name_table`.

BaseFormSet.as_ul()

  Renderar formuläret med mallen `template_name_ul`.

Att använda en formset i en vy skiljer sig inte mycket från att använda en vanlig `Form`-klass. Det enda du vill vara medveten om är att se till att använda hanteringsformuläret inuti mallen. Låt oss titta på ett exempel på en vy:

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

Mallen `manage_articles.html` kan se ut så här:

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

Det finns dock en liten genväg till ovanstående genom att låta själva formuläret hantera hanteringsformuläret:

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

Ovanstående anropar metoden `BaseFormSet.render()` på klassen formset. Detta renderar formuläret med hjälp av den mall som anges av attributet `template_name`. På samma sätt som för formulär, kommer formuläret som standard att renderas `as_div`, med andra hjälpmetoder för `as_p`, `as_ul` och `as_table` tillgängliga. Renderingen av formuläret kan anpassas genom att ange attributet `template_name`, eller mer allmänt genom overriding the default template.

### Manuellt återgivna `can_delete` och `can_order`

Om du manuellt renderar fält i mallen kan du rendera parametern `can_delete` med `{{ 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>

På samma sätt, om formuläret har möjlighet att beställa (`can_order=True`), är det möjligt att rendera det med `{{ form.ORDER }}`.

### Använda mer än en formuläruppsättning i en vy

Du kan använda mer än en formuläruppsättning i en vy om du vill. Formsets lånar mycket av sitt beteende från formulär. Med detta sagt kan du använda `prefix` för att prefixera formulärfältsnamn med ett givet värde så att mer än ett formulär kan skickas till en vy utan att namnen krockar. Låt oss ta en titt på hur detta kan åstadkommas:

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

Du skulle sedan rendera formuläruppsättningarna som normalt. Det är viktigt att påpeka att du måste skicka `prefix` i både POST- och icke-POST-fallen så att det renderas och bearbetas korrekt.

Varje formuläruppsättnings prefix ersätter standardprefixet `form` som läggs till i varje fälts HTML-attribut `name` och `id`.