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:
>>> 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)
Formuläruppsättningar kan itereras och indexeras, vilket ger åtkomst till formulären i den ordning de skapades. Du kan ändra ordningen på formulären genom att åsidosätta standardbeteendet för iteration och indexing om det behövs.
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:
>>> 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:
>>> 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.
Som standard påverkar max_num endast hur många formulär som visas och påverkar inte valideringen. Om validate_max=True skickas till formset_factory(), så kommer max_num att påverka valideringen. Se 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:
>>> 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:
>>> 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()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:
>>> # 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ä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:
>>> 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:
>>> 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ä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å:
>>> 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ä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.
>>> 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.
>>> 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:
>>> 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_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:
>>> 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:
>>> 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:
>>> 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().
Om du anropar formset.save(commit=False) kommer objekten inte att raderas automatiskt. Du måste anropa delete() på var och en av formset.deleted_objects för att faktiskt radera dem:
>>> 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:
>>> 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:
>>> 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:
>>> 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:
>>> 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ä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:
<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 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äretsas_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äretsas_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äretsas_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äretsas_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 metodernaas_div(),as_p(),as_ul()ochas_table(). Alla argument är valfria och kommer som standard att vara:template_name:template_namecontext: Värde som returneras avget_context()renderer: Värde som returneras avrenderer
- 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:
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_orderLink to this heading
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 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:
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.