FormsatserLink to this heading

class BaseFormSetLink to this definition

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:

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

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

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

Python console
>>> 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 __iter__() and __getitem__() methods if needed. (For more information on implementing these methods, see the Python documentation on sequences.)

Använda initiala data med en formulärsetLink to this heading

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:

Python console
>>> 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=[...]).

Begränsa det maximala antalet formulärLink to this heading

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

Python console
>>> 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_numNone (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ärLink to this heading

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:

Python console
>>> 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ärLink to this heading

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:

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

Python console
>>> 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()Link to this definition

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

Python console
>>> # 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):

Python console
>>> 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äretLink to this heading

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:

Python console
>>> 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).

total_form_count och initial_form_countLink to this heading

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ärLink to this heading

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.

FelmeddelandenLink to this heading

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:

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

Python console
>>> 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ärLink to this heading

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

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

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

Validering av antalet formulär i en formuläruppsättningLink to this heading

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_maxLink to this heading

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.

Python console
>>> 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.

validera_minLink to this heading

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.

Python console
>>> 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.

Hantering av beställning och borttagning av formulärLink to this heading

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ällaLink to this heading

BaseFormSet.can_orderLink to this definition

Standard: False

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

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

Python console
>>> 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_widgetLink to this heading

BaseFormSet.ordering_widgetLink to this definition

Standard: NumberInput

Ange ordering_widget för att specificera widgetklassen som ska användas med can_order:

Python console
>>> 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ällningswidgetLink to this heading

BaseFormSet.get_ordering_widget()Link to this definition

Åsidosätt get_ordering_widget() om du behöver tillhandahålla en widgetinstans för användning med can_order:

Python console
>>> 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 raderaLink to this heading

BaseFormSet.can_deleteLink to this definition

Standard: False

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

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

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

Python console
>>> 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_widgetLink to this heading

BaseFormSet.deletion_widgetLink to this definition

Standard: CheckboxInput

Ange deletion_widget för att specificera widgetklassen som ska användas med can_delete:

Python console
>>> 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_widgetLink to this heading

BaseFormSet.get_deletion_widget()Link to this definition

Åsidosätt get_deletion_widget() om du behöver tillhandahålla en widgetinstans för användning med can_delete:

Python console
>>> 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_extraLink to this heading

BaseFormSet.can_delete_extraLink to this definition

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ättningLink to this heading

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:

Python console
>>> 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ärLink to this heading

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

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

Python console
>>> 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ärLink to this heading

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:

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

Html
<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 mallarLink to this heading

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

BaseFormSet.rendererLink to this definition

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

BaseFormSet.template_nameLink to this definition

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_divLink to this definition

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_pLink to this definition

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_tableLink to this definition

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_ulLink to this definition

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()Link to this definition

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)Link to this definition

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:

BaseFormSet.as_div()Link to this definition

Renderar formuläret med mallen template_name_div.

BaseFormSet.as_p()Link to this definition

Renderar formuläret med mallen template_name_p.

BaseFormSet.as_table()Link to this definition

Renderar formuläret med mallen template_name_table.

BaseFormSet.as_ul()Link to this definition

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:

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

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

Django template
<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_orderLink to this heading

Om du manuellt renderar fält i mallen kan du rendera parametern can_delete med {{ form.DELETE }}:

Django template
<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 vyLink to this heading

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:

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