make sure that an add form that is filled out but marked for deletion doesn t cause validation errors one existing untouched and two new unvalid forms make sure this form doesn t pass validation then make sure that it does pass validation and delete the object even though the data in new forms aren t actually valid make sure that a change form that is filled out but marked for deletion doesn t cause validation errors make sure this form doesn t pass validation then make sure that it does pass validation and delete the object even though the data isn t actually valid simulate deletion of an object that doesn t exist in the database the formset is valid even though poem pk 1 doesn t exist because it s marked for deletion anyway make sure the save went through correctly regression for 19733 message calling modelformsetfactory without defining fields or exclude explicitly is prohibited with self assertraisesmessageimproperlyconfigured message modelformsetfactory def testsimplesaveself qs objects all formset modelformsetfactory fieldsall extra3 formset formsetquerysetqs self assertequallenformset forms 3 self asserthtmlequal formset forms0 asp plabel foridform0namename label input ididform0name typetext nameform0name maxlength100 input typehidden nameform0id ididform0idp self asserthtmlequal formset forms1 asp plabel foridform1namename label input ididform1name typetext nameform1name maxlength100 input typehidden nameform1id ididform1idp self asserthtmlequal formset forms2 asp plabel foridform2namename label input ididform2name typetext nameform2name maxlength100 input typehidden nameform2id ididform2idp data formtotalforms 3 the number of forms rendered forminitialforms 0 the number of forms with initial data formmaxnumforms the max number of forms form0name charles baudelaire form1name arthur rimbaud form2name formset formsetdatadata querysetqs self asserttrueformset isvalid saved formset save self assertequallensaved 2 1 2 saved self assertequal1 objects getnamecharles baudelaire self assertequal2 objects getnamearthur rimbaud s list objects orderbyname self assertequals 2 1 gah we forgot paul verlaine let s create a formset to edit the existing s with an extra form to add him we could pass in a queryset to restrict the objects we edit but in this case we ll use it to display them in alphabetical order by name qs objects orderbyname formset modelformsetfactory fieldsall extra1 candeletefalse formset formsetquerysetqs self assertequallenformset forms 3 self asserthtmlequal formset forms0 asp plabel foridform0namename label input ididform0name typetext nameform0name valuearthur rimbaud maxlength100 input typehidden nameform0id valued ididform0idp 2 id self asserthtmlequal formset forms1 asp plabel foridform1namename label input ididform1name typetext nameform1name valuecharles baudelaire maxlength100 input typehidden nameform1id valued ididform1idp 1 id self asserthtmlequal formset forms2 asp plabel foridform2namename label input ididform2name typetext nameform2name maxlength100 input typehidden nameform2id ididform2idp data formtotalforms 3 the number of forms rendered forminitialforms 2 the number of forms with initial data formmaxnumforms the max number of forms form0id str2 id form0name arthur rimbaud form1id str1 id form1name charles baudelaire form2name paul verlaine formset formsetdatadata querysetqs self asserttrueformset isvalid only changed or new objects are returned from formset save saved formset save self assertequallensaved 1 3 saved0 self assertequal3 objects getnamepaul verlaine s list objects orderbyname self assertequals 2 1 3 this probably shouldn t happen but it will if an add form was marked for deletion make sure we don t save that form qs objects orderbyname formset modelformsetfactory fieldsall extra1 candeletetrue formset formsetquerysetqs self assertequallenformset forms 4 self asserthtmlequal formset forms0 asp plabel foridform0namename label input ididform0name typetext nameform0name valuearthur rimbaud maxlength100p plabel foridform0deletedelete label input typecheckbox nameform0delete ididform0delete input typehidden nameform0id valued ididform0idp 2 id self asserthtmlequal formset forms1 asp plabel foridform1namename label input ididform1name typetext nameform1name valuecharles baudelaire maxlength100p plabel foridform1deletedelete label input typecheckbox nameform1delete ididform1delete input typehidden nameform1id valued ididform1idp 1 id self asserthtmlequal formset forms2 asp plabel foridform2namename label input ididform2name typetext nameform2name valuepaul verlaine maxlength100p plabel foridform2deletedelete label input typecheckbox nameform2delete ididform2delete input typehidden nameform2id valued ididform2idp 3 id self asserthtmlequal formset forms3 asp plabel foridform3namename label input ididform3name typetext nameform3name maxlength100 pplabel foridform3deletedelete label input typecheckbox nameform3delete ididform3delete input typehidden nameform3id ididform3idp data formtotalforms 4 the number of forms rendered forminitialforms 3 the number of forms with initial data formmaxnumforms the max number of forms form0id str2 id form0name arthur rimbaud form1id str1 id form1name charles baudelaire form2id str3 id form2name paul verlaine form3name walt whitman form3delete on formset formsetdatadata querysetqs self asserttrueformset isvalid no objects were changed or saved so nothing will come back self assertequalformset save s list objects orderbyname self assertequals 2 1 3 let s edit a record to ensure save only returns that one record data formtotalforms 4 the number of forms rendered forminitialforms 3 the number of forms with initial data formmaxnumforms the max number of forms form0id str2 id form0name walt whitman form1id str1 id form1name charles baudelaire form2id str3 id form2name paul verlaine form3name form3delete formset formsetdatadata querysetqs self asserttrueformset isvalid one record has changed saved formset save self assertequallensaved 1 self assertequalsaved0 objects getnamewalt whitman def testcommitfalseself test the behavior of commitfalse and savem2m 1 objects createnamecharles baudelaire 2 objects createnamepaul verlaine 3 objects createnamewalt whitman meeting meeting objects createcreateddate today meeting s set objects all create an instance to add to the meeting 4 objects createnamejohn steinbeck meetingformset modelformsetfactory meeting fieldsall extra1 candeletetrue data formtotalforms 2 the number of forms rendered forminitialforms 1 the number of forms with initial data formmaxnumforms the max number of forms form0id strmeeting id form0name 2nd tuesday of the week meeting form0s 2 id 1 id 3 id 4 id form1name form1s form1delete formset meetingformsetdatadata querysetmeeting objects all self asserttrueformset isvalid instances formset savecommitfalse for instance in instances instance created date today instance save formset savem2m self assertsequenceequal instances0 s all 1 4 2 3 def testmaxnumself test the behavior of maxnum with model formsets it should allow all existing related objectsinlines for a given object to be displayed but not allow the creation of new inlines beyond maxnum a1 objects createnamecharles baudelaire a2 objects createnamepaul verlaine a3 objects createnamewalt whitman qs objects orderbyname formset modelformsetfactory fieldsall maxnumnone extra3 formset formsetquerysetqs self assertequallenformset forms 6 self assertequallenformset extraforms 3 formset modelformsetfactory fieldsall maxnum4 extra3 formset formsetquerysetqs self assertequallenformset forms 4 self assertequallenformset extraforms 1 formset modelformsetfactory fieldsall maxnum0 extra3 formset formsetquerysetqs self assertequallenformset forms 3 self assertequallenformset extraforms 0 formset modelformsetfactory fieldsall maxnumnone formset formsetquerysetqs self assertsequenceequalformset getqueryset a1 a2 a3 formset modelformsetfactory fieldsall maxnum0 formset formsetquerysetqs self assertsequenceequalformset getqueryset a1 a2 a3 formset modelformsetfactory fieldsall maxnum4 formset formsetquerysetqs self assertsequenceequalformset getqueryset a1 a2 a3 def testminnumself test the behavior of minnum with model formsets it should be added to extra qs objects none formset modelformsetfactory fieldsall extra0 formset formsetquerysetqs self assertequallenformset forms 0 formset modelformsetfactory fieldsall minnum1 extra0 formset formsetquerysetqs self assertequallenformset forms 1 formset modelformsetfactory fieldsall minnum1 extra1 formset formsetquerysetqs self assertequallenformset forms 2 def testminnumwithexistingself test the behavior of minnum with existing objects objects createnamecharles baudelaire qs objects all formset modelformsetfactory fieldsall extra0 minnum1 formset formsetquerysetqs self assertequallenformset forms 1 def testcustomsavemethodself class poetformforms modelform def saveself committrue change the name to vladimir mayakovsky just to be a jerk super savecommitfalse name vladimir mayakovsky if commit save return poetformset modelformsetfactorypoet fieldsall formpoetform data formtotalforms 3 the number of forms rendered forminitialforms 0 the number of forms with initial data formmaxnumforms the max number of forms form0name walt whitman form1name charles baudelaire form2name qs poet objects all formset poetformsetdatadata querysetqs self asserttrueformset isvalid poets formset save self assertequallenpoets 2 poet1 poet2 poets self assertequalpoet1 name vladimir mayakovsky self assertequalpoet2 name vladimir mayakovsky def testcustomformself class postform1forms modelform class meta model post fields title posted class postform2forms modelform class meta model post exclude subtitle postformset modelformsetfactorypost formpostform1 formset postformset self assertnotinsubtitle formset forms0 fields postformset modelformsetfactorypost formpostform2 formset postformset self assertnotinsubtitle formset forms0 fields def testcustomquerysetinitself objects createnamecharles baudelaire objects createnamepaul verlaine class baseformsetbasemodelformset def initself args kwargs super initargs kwargs self queryset objects filternamestartswithcharles formset modelformsetfactory fieldsall formsetbaseformset formset formset self assertequallenformset getqueryset 1 def testmodelinheritanceself betterformset modelformsetfactorybetter fieldsall formset betterformset self assertequallenformset forms 1 self asserthtmlequal formset forms0 asp plabel foridform0namename label input ididform0name typetext nameform0name maxlength100 pplabel foridform0writespeedwrite speed label input typenumber nameform0writespeed ididform0writespeed input typehidden nameform0ptr ididform0ptr p data formtotalforms 1 the number of forms rendered forminitialforms 0 the number of forms with initial data formmaxnumforms the max number of forms form0ptr form0name ernest hemingway form0writespeed 10 formset betterformsetdata self asserttrueformset isvalid saved formset save self assertequallensaved 1 1 saved self assertequal1 better objects getnameernest hemingway hemingwayid better objects getnameernest hemingway pk formset betterformset self assertequallenformset forms 2 self asserthtmlequal formset forms0 asp plabel foridform0namename label input ididform0name typetext nameform0name valueernest hemingway maxlength100p plabel foridform0writespeedwrite speed label input typenumber nameform0writespeed value10 ididform0writespeed input typehidden nameform0ptr valued ididform0ptrp hemingwayid self asserthtmlequal formset forms1 asp plabel foridform1namename label input ididform1name typetext nameform1name maxlength100 pplabel foridform1writespeedwrite speed label input typenumber nameform1writespeed ididform1writespeed input typehidden nameform1ptr ididform1ptr p data formtotalforms 2 the number of forms rendered forminitialforms 1 the number of forms with initial data formmaxnumforms the max number of forms form0ptr hemingwayid form0name ernest hemingway form0writespeed 10 form1ptr form1name form1writespeed formset betterformsetdata self asserttrueformset isvalid self assertequalformset save def testinlineformsetsself we can also create a formset that is tied to a parent model this is how the admin system s edit inline functionality works booksformset inlineformsetfactory book candeletefalse extra3 fieldsall objects createnamecharles baudelaire formset booksformsetinstance self assertequallenformset forms 3 self asserthtmlequal formset forms0 asp plabel foridbookset0titletitle label input ididbookset0title typetext namebookset0title maxlength100 input typehidden namebookset0 valued ididbookset0 input typehidden namebookset0id ididbookset0id p id self asserthtmlequal formset forms1 asp plabel foridbookset1titletitle label input ididbookset1title typetext namebookset1title maxlength100 input typehidden namebookset1 valued ididbookset1 input typehidden namebookset1id ididbookset1idp id self asserthtmlequal formset forms2 asp plabel foridbookset2titletitle label input ididbookset2title typetext namebookset2title maxlength100 input typehidden namebookset2 valued ididbookset2 input typehidden namebookset2id ididbookset2idp id data booksettotalforms 3 the number of forms rendered booksetinitialforms 0 the number of forms with initial data booksetmaxnumforms the max number of forms bookset0title les fleurs du mal bookset1title bookset2title formset booksformsetdata instance self asserttrueformset isvalid saved formset save self assertequallensaved 1 book1 saved self assertequalbook1 book objects gettitleles fleurs du mal self assertsequenceequal bookset all book1 now that we ve added a book to charles baudelaire let s try adding another one this time though an edit form will be available for every existing book booksformset inlineformsetfactory book candeletefalse extra2 fieldsall objects getnamecharles baudelaire formset booksformsetinstance self assertequallenformset forms 3 self asserthtmlequal formset forms0 asp plabel foridbookset0titletitle label input ididbookset0title typetext namebookset0title valueles fleurs du mal maxlength100 input typehidden namebookset0 valued ididbookset0 input typehidden namebookset0id valued ididbookset0idp id book1 id self asserthtmlequal formset forms1 asp plabel foridbookset1titletitle label input ididbookset1title typetext namebookset1title maxlength100 input typehidden namebookset1 valued ididbookset1 input typehidden namebookset1id ididbookset1idp id self asserthtmlequal formset forms2 asp plabel foridbookset2titletitle label input ididbookset2title typetext namebookset2title maxlength100 input typehidden namebookset2 valued ididbookset2 input typehidden namebookset2id ididbookset2idp id data booksettotalforms 3 the number of forms rendered booksetinitialforms 1 the number of forms with initial data booksetmaxnumforms the max number of forms bookset0id strbook1 id bookset0title les fleurs du mal bookset1title les paradis artificiels bookset2title formset booksformsetdata instance self asserttrueformset isvalid saved formset save self assertequallensaved 1 book2 saved self assertequalbook2 book objects gettitleles paradis artificiels as you can see les paradis artificiels is now a book belonging to charles baudelaire self assertsequenceequal bookset orderbytitle book1 book2 def testinlineformsetssaveasnewself the saveasnew parameter lets you reassociate the data to a new instance this is used in the admin for saveas functionality booksformset inlineformsetfactory book candeletefalse extra2 fieldsall objects createnamecharles baudelaire an immutable querydict simulates request post data querydictmutabletrue data update booksettotalforms 3 the number of forms rendered booksetinitialforms 2 the number of forms with initial data booksetmaxnumforms the max number of forms bookset0id 1 bookset0title les fleurs du mal bookset1id 2 bookset1title les paradis artificiels bookset2title data mutable false formset booksformsetdata instance saveasnewtrue self asserttrueformset isvalid self assertisdata mutable false new objects createnamecharles baudelaire formset booksformsetdata instancenew saveasnewtrue saved formset save self assertequallensaved 2 book1 book2 saved self assertequalbook1 title les fleurs du mal self assertequalbook2 title les paradis artificiels test using a custom prefix on an inline formset formset booksformsetprefixtest self assertequallenformset forms 2 self asserthtmlequal formset forms0 asp plabel foridtest0titletitle label input ididtest0title typetext nametest0title maxlength100 input typehidden nametest0 ididtest0 input typehidden nametest0id ididtest0idp self asserthtmlequal formset forms1 asp plabel foridtest1titletitle label input ididtest1title typetext nametest1title maxlength100 input typehidden nametest1 ididtest1 input typehidden nametest1id ididtest1idp def testinlineformsetswithcustompkself test inline formsets where the inlineedited object has a custom primary key that is not the fk to the parent object self maxdiff 1024 booksformset2 inlineformsetfactory bookwithcustompk candeletefalse extra1 fieldsall objects createpk1 namecharles baudelaire formset booksformset2instance self assertequallenformset forms 1 self asserthtmlequal formset forms0 asp plabel foridbookwithcustompkset0mypkmy pk label input ididbookwithcustompkset0mypk typenumber namebookwithcustompkset0mypk step1p plabel foridbookwithcustompkset0titletitle label input ididbookwithcustompkset0title typetext namebookwithcustompkset0title maxlength100 input typehidden namebookwithcustompkset0 value1 ididbookwithcustompkset0p data the number of forms rendered bookwithcustompksettotalforms 1 the number of forms with initial data bookwithcustompksetinitialforms 0 the max number of forms bookwithcustompksetmaxnumforms bookwithcustompkset0mypk 77777 bookwithcustompkset0title les fleurs du mal formset booksformset2data instance self asserttrueformset isvalid saved formset save self assertequallensaved 1 book1 saved self assertequalbook1 pk 77777 book1 bookwithcustompkset get self assertequalbook1 title les fleurs du mal def testinlineformsetswithmultitableinheritanceself test inline formsets where the inlineedited object uses multitable inheritance thus has a non autofield yet autocreated primary key booksformset3 inlineformsetfactory alternatebook candeletefalse extra1 fieldsall objects createpk1 namecharles baudelaire formset booksformset3instance self assertequallenformset forms 1 self asserthtmlequal formset forms0 asp plabel foridalternatebookset0titletitle label input ididalternatebookset0title typetext namealternatebookset0title maxlength100p plabel foridalternatebookset0notesnotes label input ididalternatebookset0notes typetext namealternatebookset0notes maxlength100 input typehidden namealternatebookset0 value1 ididalternatebookset0 input typehidden namealternatebookset0bookptr ididalternatebookset0bookptrp data the number of forms rendered alternatebooksettotalforms 1 the number of forms with initial data alternatebooksetinitialforms 0 the max number of forms alternatebooksetmaxnumforms alternatebookset0title flowers of evil alternatebookset0notes english translation of les fleurs du mal formset booksformset3data instance self asserttrueformset isvalid saved formset save self assertequallensaved 1 book1 saved self assertequalbook1 title flowers of evil self assertequalbook1 notes english translation of les fleurs du mal skipunlessdbfeaturesupportspartiallynullableuniqueconstraints def testinlineformsetswithnullableuniquetogetherself test inline formsets where the inlineedited object has a uniquetogether constraint with a nullable member booksformset4 inlineformsetfactory bookwithoptionalalteditor candeletefalse extra2 fieldsall objects createpk1 namecharles baudelaire data the number of forms rendered bookwithoptionalalteditorsettotalforms 2 the number of forms with initial data bookwithoptionalalteditorsetinitialforms 0 the max number of forms bookwithoptionalalteditorsetmaxnumforms bookwithoptionalalteditorset0 1 bookwithoptionalalteditorset0title les fleurs du mal bookwithoptionalalteditorset1 1 bookwithoptionalalteditorset1title les fleurs du mal formset booksformset4data instance self asserttrueformset isvalid saved formset save self assertequallensaved 2 book1 book2 saved self assertequalbook1 id 1 self assertequalbook1 title les fleurs du mal self assertequalbook2 id 1 self assertequalbook2 title les fleurs du mal def testinlineformsetswithcustomsavemethodself booksformset inlineformsetfactory book candeletefalse extra2 fieldsall objects createpk1 namecharles baudelaire book1 book objects create pk1 titleles paradis artificiels book2 book objects createpk2 titleles fleurs du mal book3 book objects createpk3 titleflowers of evil class poemformforms modelform def saveself committrue change the name to brooklyn bridge just to be a jerk poem super savecommitfalse poem name brooklyn bridge if commit poem save return poem poemformset inlineformsetfactorypoet poem formpoemform fieldsall data poemsettotalforms 3 the number of forms rendered poemsetinitialforms 0 the number of forms with initial data poemsetmaxnumforms the max number of forms poemset0name the cloud in trousers poemset1name i poemset2name poet poet objects createnamevladimir mayakovsky formset poemformsetdatadata instancepoet self asserttrueformset isvalid saved formset save self assertequallensaved 2 poem1 poem2 saved self assertequalpoem1 name brooklyn bridge self assertequalpoem2 name brooklyn bridge we can provide a custom queryset to our inlineformset customqs book objects orderbytitle formset booksformsetinstance querysetcustomqs self assertequallenformset forms 5 self asserthtmlequal formset forms0 asp plabel foridbookset0titletitle label input ididbookset0title typetext namebookset0title valueles paradis artificiels maxlength100 input typehidden namebookset0 value1 ididbookset0 input typehidden namebookset0id value1 ididbookset0id p self asserthtmlequal formset forms1 asp plabel foridbookset1titletitle label input ididbookset1title typetext namebookset1title valueles fleurs du mal maxlength100 input typehidden namebookset1 value1 ididbookset1 input typehidden namebookset1id value2 ididbookset1id p self asserthtmlequal formset forms2 asp plabel foridbookset2titletitle label input ididbookset2title typetext namebookset2title valueflowers of evil maxlength100 input typehidden namebookset2 value1 ididbookset2 input typehidden namebookset2id value3 ididbookset2idp self asserthtmlequal formset forms3 asp plabel foridbookset3titletitle label input ididbookset3title typetext namebookset3title maxlength100 input typehidden namebookset3 value1 ididbookset3 input typehidden namebookset3id ididbookset3idp self asserthtmlequal formset forms4 asp plabel foridbookset4titletitle label input ididbookset4title typetext namebookset4title maxlength100 input typehidden namebookset4 value1 ididbookset4 input typehidden namebookset4id ididbookset4idp data booksettotalforms 5 the number of forms rendered booksetinitialforms 3 the number of forms with initial data booksetmaxnumforms the max number of forms bookset0id strbook1 id bookset0title les paradis artificiels bookset1id strbook2 id bookset1title les fleurs du mal bookset2id strbook3 id bookset2title flowers of evil bookset3title revue des deux mondes bookset4title formset booksformsetdata instance querysetcustomqs self asserttrueformset isvalid customqs book objects filtertitlestartswithf formset booksformsetinstance querysetcustomqs self asserthtmlequal formset forms0 asp plabel foridbookset0titletitle label input ididbookset0title typetext namebookset0title valueflowers of evil maxlength100 input typehidden namebookset0 value1 ididbookset0 input typehidden namebookset0id value3 ididbookset0idp self asserthtmlequal formset forms1 asp plabel foridbookset1titletitle label input ididbookset1title typetext namebookset1title maxlength100 input typehidden namebookset1 value1 ididbookset1 input typehidden namebookset1id ididbookset1idp self asserthtmlequal formset forms2 asp plabel foridbookset2titletitle label input ididbookset2title typetext namebookset2title maxlength100 input typehidden namebookset2 value1 ididbookset2 input typehidden namebookset2id ididbookset2idp data booksettotalforms 3 the number of forms rendered booksetinitialforms 1 the number of forms with initial data booksetmaxnumforms the max number of forms bookset0id strbook3 id bookset0title flowers of evil bookset1title revue des deux mondes bookset2title formset booksformsetdata instance querysetcustomqs self asserttrueformset isvalid def testinlineformsetswithcustomsavemethodrelatedinstanceself class poemform2forms modelform def saveself committrue poem super savecommitfalse poem name s by s poem name poem poet name if commit poem save return poem poemformset inlineformsetfactory poet poem formpoemform2 fieldsall data poemsettotalforms 1 poemsetinitialforms 0 poemsetmaxnumforms poemset0name le lac poet poet formset poemformsetdatadata instancepoet self asserttrueformset isvalid the poet instance is saved after the formset instantiation this happens in admin s changeformview when adding a new object and some inlines in the same request poet name lamartine poet save poem formset save0 self assertequalpoem name le lac by lamartine def testinlineformsetswithwrongfknameself we need to ensure that it is displayed custom primary keys with foreignkey onetoonefield and autofield a custom primary key that is a foreignkey or onetoonefield get rendered for the user to choose foreignkey with uniquetrue should enforce maxnum1 if validatemax is set and maxnum is less than totalforms in the data then throw an exception maxnumforms in the data is irrelevant here it s output as a hint for the client but its value in the returned data is not checked now test the same thing without the validatemax flag to ensure default behavior is unchanged also see bug 8882 attempt to save the same revision against the same repo uniquetogether with inlineformsetfactory with overridden form fields also see 9494 use of callable defaults see bug 7975 django will render a hidden field for model fields that have a callable default this is required to ensure the value is tested for change correctly when determine what extra forms have changed to save test for validation with callable defaults validations rely on hidden fields now test for when the data changes now test with split datetime fields inlineformsetfactory tests with fk having nulltrue see 9462 create some data that will exhibit the issue a proxy for django contrib postgres forms simplearrayfield def topythonself value value super topythonvalue return value split if value else class bookformforms modelform title simplearrayfield class meta model book fields title bookformset inlineformsetfactory book formbookform data booksettotalforms 3 booksetinitialforms 0 booksetmaxnumforms bookset0title test1 test2 bookset1title test1 test2 bookset2title test3 test4 objects createnametest formset bookformsetdata instance self assertequal formset errors all please correct the duplicate values below def testmodelformsetwithcustompkself a formset for a model that has a custom primary key that still needs to be added to the formset automatically formset modelformsetfactory classymexicanrestaurant fieldstacosareyummy self assertequal sortedformset forms0 fields tacosareyummy therestaurant def testmodelformsetwithinitialmodelinstanceself haschanged should compare model instance and primary key see 18898 formset modelformsetfactorypoem fieldsall johnmilton poetnamejohn milton johnmilton save data formtotalforms 1 forminitialforms 0 formmaxnumforms form0name form0poet strjohnmilton id formset formsetinitialpoet johnmilton datadata self assertfalseformset extraforms0 haschanged def testmodelformsetwithinitialquerysetself haschanged should work with queryset and list of pk s see 18898 formset modelformsetfactorymeeting fieldsall objects createpk1 namecharles baudelaire data formtotalforms 1 forminitialforms 0 formmaxnumforms form0name form0created form0s list objects valueslistid flattrue formset formsetinitials objects all datadata self assertfalseformset extraforms0 haschanged def testpreventduplicatesfromwiththesameformsetself formset modelformsetfactoryproduct fieldsall extra2 data formtotalforms 2 forminitialforms 0 formmaxnumforms form0slug redcar form1slug redcar formset formsetdata self assertfalseformset isvalid self assertequal formset nonformerrors please correct the duplicate data for slug formset modelformsetfactoryprice fieldsall extra2 data formtotalforms 2 forminitialforms 0 formmaxnumforms form0price 25 form0quantity 7 form1price 25 form1quantity 7 formset formsetdata self assertfalseformset isvalid self assertequal formset nonformerrors please correct the duplicate data for price and quantity which must be unique only the price field is specified this should skip any unique checks since the uniquetogether is not fulfilled this will fail with a keyerror if broken formset modelformsetfactoryprice fieldsprice extra2 data formtotalforms 2 forminitialforms 0 formmaxnumforms form0price 24 form1price 24 formset formsetdata self asserttrueformset isvalid formset inlineformsetfactory book extra0 fieldsall objects createpk1 namecharles baudelaire book objects createpk1 titleles paradis artificiels book objects createpk2 titleles fleurs du mal book objects createpk3 titleflowers of evil bookids bookset orderbyid valueslistid flattrue data booksettotalforms 2 booksetinitialforms 2 booksetmaxnumforms bookset0title the 2008 election bookset0 str id bookset0id strbookids0 bookset1title the 2008 election bookset1 str id bookset1id strbookids1 formset formsetdatadata instance self assertfalseformset isvalid self assertequal formset nonformerrors please correct the duplicate data for title self assertequal formset errors all please correct the duplicate values below formset modelformsetfactorypost fieldsall extra2 data formtotalforms 2 forminitialforms 0 formmaxnumforms form0title blah form0slug morning form0subtitle foo form0posted 20090101 form1title blah form1slug morning in prague form1subtitle rawr form1posted 20090101 formset formsetdata self assertfalseformset isvalid self assertequal formset nonformerrors please correct the duplicate data for title which must be unique for the date in posted self assertequal formset errors all please correct the duplicate values below data formtotalforms 2 forminitialforms 0 formmaxnumforms form0title foo form0slug morning in prague form0subtitle foo form0posted 20090101 form1title blah form1slug morning in prague form1subtitle rawr form1posted 20090802 formset formsetdata self assertfalseformset isvalid self assertequal formset nonformerrors please correct the duplicate data for slug which must be unique for the year in posted data formtotalforms 2 forminitialforms 0 formmaxnumforms form0title foo form0slug morning in prague form0subtitle rawr form0posted 20080801 form1title blah form1slug prague form1subtitle rawr form1posted 20090802 formset formsetdata self assertfalseformset isvalid self assertequal formset nonformerrors please correct the duplicate data for subtitle which must be unique for the month in posted def testpreventchangeoutermodelandcreateinvaliddataself objects createnamecharles other objects createnamewalt formset modelformsetfactory fieldsall data formtotalforms 2 forminitialforms 2 formmaxnumforms form0id str id form0name charles form1id strother id a model not in the formset s queryset form1name changed name this formset is only for walt whitman and shouldn t accept data for other formset formset datadata queryset objects filteridin id self asserttrueformset isvalid formset save the name of other shouldn t be changed and new models aren t created self assertsequenceequal objects all other def testvalidationwithoutidself formset modelformsetfactory fieldsall data formtotalforms 1 forminitialforms 1 formmaxnumforms form0name charles formset formsetdata self assertequal formset errors id this field is required def testvalidationwithchildmodelwithoutidself betterformset modelformsetfactorybetter fieldsall data formtotalforms 1 forminitialforms 1 formmaxnumforms form0name charles form0writespeed 10 formset betterformsetdata self assertequal formset errors ptr this field is required def testvalidationwithinvalididself formset modelformsetfactory fieldsall data formtotalforms 1 forminitialforms 1 formmaxnumforms form0id abc form0name charles formset formsetdata self assertequal formset errors id select a valid choice that choice is not one of the available choices def testvalidationwithnonexistentidself formset modelformsetfactory fieldsall data formtotalforms 1 forminitialforms 1 formmaxnumforms form0id 12345 form0name charles formset formsetdata self assertequal formset errors id select a valid choice that choice is not one of the available choices def testinitialformcountemptydataself formset modelformsetfactory fieldsall formset formset self assertequalformset initialformcount 0 def testeditonlyself charles objects createnamecharles baudelaire formset modelformsetfactory fieldsall editonlytrue data formtotalforms 2 forminitialforms 0 formmaxnumforms 0 form0name arthur rimbaud form1name walt whitman formset formsetdata self assertisformset isvalid true formset save self assertsequenceequal objects all charles data formtotalforms 2 forminitialforms 1 formmaxnumforms 0 form0id charles pk form0name arthur rimbaud form1name walt whitman formset formsetdata self assertisformset isvalid true formset save charles refreshfromdb self assertequalcharles name arthur rimbaud self assertsequenceequal objects all charles def testeditonlyinlineformsetfactoryself charles objects createnamecharles baudelaire book book objects createcharles titleles paradis artificiels formset inlineformsetfactory book candeletefalse fieldsall editonlytrue data booksettotalforms 4 booksetinitialforms 1 booksetmaxnumforms 0 bookset0id book pk bookset0title les fleurs du mal bookset0 charles pk bookset1title flowers of evil bookset1 charles pk formset formsetdata instancecharles self assertisformset isvalid true formset save book refreshfromdb self assertequalbook title les fleurs du mal self assertsequenceequalbook objects all book def testeditonlyobjectoutsideofquerysetself charles objects createnamecharles baudelaire walt objects createnamewalt whitman data formtotalforms 1 forminitialforms 1 form0id walt pk form0name parth patil formset modelformsetfactory fieldsall editonlytrue formset formsetdata queryset objects filterpkcharles pk self assertisformset isvalid true formset save self assertcountequal objects all charles walt def testeditonlyformsetfactorywithbasemodelformsetself charles objects createnamecharles baudelaire class formforms modelform class meta model fields all class baseformsetbasemodelformset def initself args kwargs self model super initargs kwargs formset formsetfactoryform formsetbaseformset data formtotalforms 2 forminitialforms 1 formmaxnumforms 0 form0id charles pk form0name shawn dong form1name walt whitman formset formsetdata self assertisformset isvalid true formset save self assertequal objects count 2 charles refreshfromdb self assertequalcharles name shawn dong self assertequal objects count 2 class testmodelformsetoverridestroughformmetatestcase def testmodelformsetfactorywidgetsself widgets name forms textinputattrsclass poet poetformset modelformsetfactorypoet fieldsall widgetswidgets form poetformset form self asserthtmlequal strformname input ididname maxlength100 typetext classpoet namename required def testinlineformsetfactorywidgetsself widgets title forms textinputattrsclass book bookformset inlineformsetfactory book widgetswidgets fieldsall form bookformset form self asserthtmlequal strformtitle input classbook ididtitle maxlength100 nametitle typetext required def testmodelformsetfactorylabelsoverridesself bookformset modelformsetfactory book fieldsall labelstitle name form bookformset form self asserthtmlequal formtitle labeltag label foridtitlename label self asserthtmlequal formtitle legendtag legend foridtitlename legend def testinlineformsetfactorylabelsoverridesself bookformset inlineformsetfactory book fieldsall labelstitle name form bookformset form self asserthtmlequal formtitle labeltag label foridtitlename label self asserthtmlequal formtitle legendtag legend foridtitlename legend def testmodelformsetfactoryhelptextoverridesself bookformset modelformsetfactory book fieldsall helptextstitle choose carefully form bookformset form self assertequalformtitle helptext choose carefully def testinlineformsetfactoryhelptextoverridesself bookformset inlineformsetfactory book fieldsall helptextstitle choose carefully form bookformset form self assertequalformtitle helptext choose carefully def testmodelformsetfactoryerrormessagesoverridesself objects createpk1 namecharles baudelaire bookformset modelformsetfactory book fieldsall errormessagestitle maxlength title too long form bookformset formdatatitle foo 30 id form fullclean self assertequalform errors title title too long def testinlineformsetfactoryerrormessagesoverridesself objects createpk1 namecharles baudelaire bookformset inlineformsetfactory book fieldsall errormessagestitle maxlength title too long form bookformset formdatatitle foo 30 id form fullclean self assertequalform errors title title too long def testmodelformsetfactoryfieldclassoverridesself objects createpk1 namecharles baudelaire bookformset modelformsetfactory book fieldsall fieldclasses title forms slugfield form bookformset formdatatitle foo 30 id self assertisbook meta getfieldtitle class models charfield self assertisinstanceform fieldstitle forms slugfield def testinlineformsetfactoryfieldclassoverridesself objects createpk1 namecharles baudelaire bookformset inlineformsetfactory book fieldsall fieldclasses title forms slugfield form bookformset formdatatitle foo 30 id self assertisbook meta getfieldtitle class models charfield self assertisinstanceform fieldstitle forms slugfield def testmodelformsetfactoryabsolutemaxself formset modelformsetfactory fieldsall absolutemax1500 data formtotalforms 1501 forminitialforms 0 formmaxnumforms 0 formset formsetdatadata self assertisformset isvalid false self assertequallenformset forms 1500 self assertequal formset nonformerrors please submit at most 1000 forms def testmodelformsetfactoryabsolutemaxwithmaxnumself formset modelformsetfactory fieldsall maxnum20 absolutemax100 data formtotalforms 101 forminitialforms 0 formmaxnumforms 0 formset formsetdatadata self assertisformset isvalid false self assertequallenformset forms 100 self assertequal formset nonformerrors please submit at most 20 forms def testinlineformsetfactoryabsolutemaxself objects createnamecharles baudelaire bookformset inlineformsetfactory book fieldsall absolutemax1500 data booksettotalforms 1501 booksetinitialforms 0 booksetmaxnumforms 0 formset bookformsetdata instance self assertisformset isvalid false self assertequallenformset forms 1500 self assertequal formset nonformerrors please submit at most 1000 forms def testinlineformsetfactoryabsolutemaxwithmaxnumself objects createnamecharles baudelaire bookformset inlineformsetfactory book fieldsall maxnum20 absolutemax100 data booksettotalforms 101 booksetinitialforms 0 booksetmaxnumforms 0 formset bookformsetdata instance self assertisformset isvalid false self assertequallenformset forms 100 self assertequal formset nonformerrors please submit at most 20 forms def testmodelformsetfactorycandeleteextraself formset modelformsetfactory fieldsall candeletetrue candeleteextratrue extra2 formset formset self assertequallenformset 2 self assertindelete formset forms0 fields self assertindelete formset forms1 fields def testmodelformsetfactorydisabledeleteextraself formset modelformsetfactory fieldsall candeletetrue candeleteextrafalse extra2 formset formset self assertequallenformset 2 self assertnotindelete formset forms0 fields self assertnotindelete formset forms1 fields def testinlineformsetfactorycandeleteextraself bookformset inlineformsetfactory book fieldsall candeletetrue candeleteextratrue extra2 formset bookformset self assertequallenformset 2 self assertindelete formset forms0 fields self assertindelete formset forms1 fields def testinlineformsetfactorycannotdeleteextraself bookformset inlineformsetfactory book fieldsall candeletetrue candeleteextrafalse extra2 formset bookformset self assertequallenformset 2 self assertnotindelete formset forms0 fields self assertnotindelete formset forms1 fields def testinlineformsetfactorypassesrendererself from django forms renderers import jinja2 renderer jinja2 bookformset inlineformsetfactory book fieldsall rendererrenderer formset bookformset self assertequalformset renderer renderer def testmodelformsetfactorypassesrendererself from django forms renderers import jinja2 renderer jinja2 bookformset modelformsetfactory fieldsall rendererrenderer formset bookformset self assertequalformset renderer renderer def testmodelformsetfactorydefaultrendererself class customrendererdjangotemplates pass class modelformwithdefaultrenderermodelform defaultrenderer customrenderer bookformset modelformsetfactory formmodelformwithdefaultrenderer fieldsall formset bookformset self assertequal formset forms0 renderer modelformwithdefaultrenderer defaultrenderer self assertequal formset emptyform renderer modelformwithdefaultrenderer defaultrenderer self assertisinstanceformset renderer djangotemplates def testinlineformsetfactorydefaultrendererself class customrendererdjangotemplates pass class modelformwithdefaultrenderermodelform defaultrenderer customrenderer bookformset inlineformsetfactory book formmodelformwithdefaultrenderer fieldsall formset bookformset self assertequal formset forms0 renderer modelformwithdefaultrenderer defaultrenderer self assertequal formset emptyform renderer modelformwithdefaultrenderer defaultrenderer self assertisinstanceformset renderer djangotemplates make sure that an add form that is filled out but marked for deletion doesn t cause validation errors one existing untouched and two new unvalid forms too long violate unique constraint make sure this form doesn t pass validation then make sure that it does pass validation and delete the object even though the data in new forms aren t actually valid make sure that a change form that is filled out but marked for deletion doesn t cause validation errors make sure this form doesn t pass validation then make sure that it does pass validation and delete the object even though the data isn t actually valid simulate deletion of an object that doesn t exist in the database doesn t exist the formset is valid even though poem pk 1 doesn t exist because it s marked for deletion anyway make sure the save went through correctly regression for 19733 the number of forms rendered the number of forms with initial data the max number of forms gah we forgot paul verlaine let s create a formset to edit the existing s with an extra form to add him we could pass in a queryset to restrict the objects we edit but in this case we ll use it to display them in alphabetical order by name the number of forms rendered the number of forms with initial data the max number of forms only changed or new objects are returned from formset save this probably shouldn t happen but it will if an add form was marked for deletion make sure we don t save that form the number of forms rendered the number of forms with initial data the max number of forms no objects were changed or saved so nothing will come back let s edit a record to ensure save only returns that one record the number of forms rendered the number of forms with initial data the max number of forms one record has changed test the behavior of commit false and save_m2m create an instance to add to the meeting the number of forms rendered the number of forms with initial data the max number of forms test the behavior of max_num with model formsets it should allow all existing related objects inlines for a given object to be displayed but not allow the creation of new inlines beyond max_num test the behavior of min_num with model formsets it should be added to extra test the behavior of min_num with existing objects change the name to vladimir mayakovsky just to be a jerk the number of forms rendered the number of forms with initial data the max number of forms model_formset_factory respects fields and exclude parameters of a custom form a queryset can be overridden in the formset s __init__ method the number of forms rendered the number of forms with initial data the max number of forms the number of forms rendered the number of forms with initial data the max number of forms we can also create a formset that is tied to a parent model this is how the admin system s edit inline functionality works the number of forms rendered the number of forms with initial data the max number of forms now that we ve added a book to charles baudelaire let s try adding another one this time though an edit form will be available for every existing book the number of forms rendered the number of forms with initial data the max number of forms as you can see les paradis artificiels is now a book belonging to charles baudelaire the save_as_new parameter lets you re associate the data to a new instance this is used in the admin for save_as functionality an immutable querydict simulates request post the number of forms rendered the number of forms with initial data the max number of forms test using a custom prefix on an inline formset test inline formsets where the inline edited object has a custom primary key that is not the fk to the parent object the number of forms rendered the number of forms with initial data the max number of forms test inline formsets where the inline edited object uses multi table inheritance thus has a non autofield yet auto created primary key the number of forms rendered the number of forms with initial data the max number of forms test inline formsets where the inline edited object has a unique_together constraint with a nullable member the number of forms rendered the number of forms with initial data the max number of forms change the name to brooklyn bridge just to be a jerk the number of forms rendered the number of forms with initial data the max number of forms we can provide a custom queryset to our inlineformset the number of forms rendered the number of forms with initial data the max number of forms the number of forms rendered the number of forms with initial data the max number of forms the modelform save method should be able to access the related object if it exists in the database 24395 the poet instance is saved after the formset instantiation this happens in admin s changeform_view when adding a new object and some inlines in the same request regression for 23451 we need to ensure that it is displayed custom primary keys with foreignkey onetoonefield and autofield a custom primary key that is a foreignkey or onetoonefield get rendered for the user to choose foreignkey with unique true should enforce max_num 1 if validate_max is set and max_num is less than total_forms in the data then throw an exception max_num_forms in the data is irrelevant here it s output as a hint for the client but its value in the returned data is not checked should be ignored now test the same thing without the validate_max flag to ensure default behavior is unchanged also see bug 8882 attempt to save the same revision against the same repo unique_together with inlineformset_factory with overridden form fields also see 9494 use of callable defaults see bug 7975 django will render a hidden field for model fields that have a callable default this is required to ensure the value is tested for change correctly when determine what extra forms have changed to save this formset only has one form test for validation with callable defaults validations rely on hidden fields now test for when the data changes now test with split datetime fields inlineformset_factory tests with fk having null true see 9462 create some data that will exhibit the issue a proxy for django contrib postgres forms simplearrayfield a formset for a model that has a custom primary key that still needs to be added to the formset automatically has_changed should compare model instance and primary key see 18898 has_changed should work with queryset and list of pk s see 18898 only the price field is specified this should skip any unique checks since the unique_together is not fulfilled this will fail with a keyerror if broken a model not in the formset s queryset this formset is only for walt whitman and shouldn t accept data for other_ the name of other_ shouldn t be changed and new models aren t created
import datetime
import re
from datetime import date
from decimal import Decimal

from django import forms
from django.core.exceptions import ImproperlyConfigured
from django.db import models
from django.forms.formsets import formset_factory
from django.forms.models import (
    BaseModelFormSet,
    ModelForm,
    _get_foreign_key,
    inlineformset_factory,
    modelformset_factory,
)
from django.forms.renderers import DjangoTemplates
from django.http import QueryDict
from django.test import TestCase, skipUnlessDBFeature

from .models import (
    AlternateBook,
    Author,
    AuthorMeeting,
    BetterAuthor,
    Book,
    BookWithCustomPK,
    BookWithOptionalAltEditor,
    ClassyMexicanRestaurant,
    CustomPrimaryKey,
    Location,
    Membership,
    MexicanRestaurant,
    Owner,
    OwnerProfile,
    Person,
    Place,
    Player,
    Poem,
    Poet,
    Post,
    Price,
    Product,
    Repository,
    Restaurant,
    Revision,
    Team,
)


class DeletionTests(TestCase):
    def test_deletion(self):
        PoetFormSet = modelformset_factory(Poet, fields="__all__", can_delete=True)
        poet = Poet.objects.create(name="test")
        data = {
            "form-TOTAL_FORMS": "1",
            "form-INITIAL_FORMS": "1",
            "form-MAX_NUM_FORMS": "0",
            "form-0-id": str(poet.pk),
            "form-0-name": "test",
            "form-0-DELETE": "on",
        }
        formset = PoetFormSet(data, queryset=Poet.objects.all())
        formset.save(commit=False)
        self.assertEqual(Poet.objects.count(), 1)

        formset.save()
        self.assertTrue(formset.is_valid())
        self.assertEqual(Poet.objects.count(), 0)

    def test_add_form_deletion_when_invalid(self):
        
        PoetFormSet = modelformset_factory(Poet, fields="__all__", can_delete=True)
        poet = Poet.objects.create(name="test")
        
        data = {
            "form-TOTAL_FORMS": "3",
            "form-INITIAL_FORMS": "1",
            "form-MAX_NUM_FORMS": "0",
            "form-0-id": str(poet.id),
            "form-0-name": "test",
            "form-1-id": "",
            "form-1-name": "x" * 1000,  
            "form-2-id": str(poet.id),  
            "form-2-name": "test2",
        }
        formset = PoetFormSet(data, queryset=Poet.objects.all())
        
        self.assertIs(formset.is_valid(), False)
        self.assertEqual(Poet.objects.count(), 1)

        
        
        data["form-0-DELETE"] = "on"
        data["form-1-DELETE"] = "on"
        data["form-2-DELETE"] = "on"
        formset = PoetFormSet(data, queryset=Poet.objects.all())
        self.assertIs(formset.is_valid(), True)
        formset.save()
        self.assertEqual(Poet.objects.count(), 0)

    def test_change_form_deletion_when_invalid(self):
        
        PoetFormSet = modelformset_factory(Poet, fields="__all__", can_delete=True)
        poet = Poet.objects.create(name="test")
        data = {
            "form-TOTAL_FORMS": "1",
            "form-INITIAL_FORMS": "1",
            "form-MAX_NUM_FORMS": "0",
            "form-0-id": str(poet.id),
            "form-0-name": "x" * 1000,
        }
        formset = PoetFormSet(data, queryset=Poet.objects.all())
        
        self.assertIs(formset.is_valid(), False)
        self.assertEqual(Poet.objects.count(), 1)

        
        
        data["form-0-DELETE"] = "on"
        formset = PoetFormSet(data, queryset=Poet.objects.all())
        self.assertIs(formset.is_valid(), True)
        formset.save()
        self.assertEqual(Poet.objects.count(), 0)

    def test_outdated_deletion(self):
        poet = Poet.objects.create(name="test")
        poem = Poem.objects.create(name="Brevity is the soul of wit", poet=poet)

        PoemFormSet = inlineformset_factory(
            Poet, Poem, fields="__all__", can_delete=True
        )

        
        data = {
            "form-TOTAL_FORMS": "2",
            "form-INITIAL_FORMS": "2",
            "form-0-id": str(poem.pk),
            "form-0-name": "foo",
            "form-1-id": str(poem.pk + 1),  
            "form-1-name": "bar",
            "form-1-DELETE": "on",
        }
        formset = PoemFormSet(data, instance=poet, prefix="form")

        
        
        self.assertTrue(formset.is_valid())

        formset.save()

        
        self.assertEqual(Poem.objects.get(pk=poem.pk).name, "foo")
        self.assertEqual(poet.poem_set.count(), 1)
        self.assertFalse(Poem.objects.filter(pk=poem.pk + 1).exists())


class ModelFormsetTest(TestCase):
    def test_modelformset_factory_without_fields(self):
        
        message = (
            "Calling modelformset_factory without defining 'fields' or 'exclude' "
            "explicitly is prohibited."
        )
        with self.assertRaisesMessage(ImproperlyConfigured, message):
            modelformset_factory(Author)

    def test_simple_save(self):
        qs = Author.objects.all()
        AuthorFormSet = modelformset_factory(Author, fields="__all__", extra=3)

        formset = AuthorFormSet(queryset=qs)
        self.assertEqual(len(formset.forms), 3)
        self.assertHTMLEqual(
            formset.forms[0].as_p(),
            '<p><label for="id_form-0-name">Name:</label>'
            '<input id="id_form-0-name" type="text" name="form-0-name" maxlength="100">'
            '<input type="hidden" name="form-0-id" id="id_form-0-id"></p>',
        )
        self.assertHTMLEqual(
            formset.forms[1].as_p(),
            '<p><label for="id_form-1-name">Name:</label>'
            '<input id="id_form-1-name" type="text" name="form-1-name" maxlength="100">'
            '<input type="hidden" name="form-1-id" id="id_form-1-id"></p>',
        )
        self.assertHTMLEqual(
            formset.forms[2].as_p(),
            '<p><label for="id_form-2-name">Name:</label>'
            '<input id="id_form-2-name" type="text" name="form-2-name" maxlength="100">'
            '<input type="hidden" name="form-2-id" id="id_form-2-id"></p>',
        )

        data = {
            "form-TOTAL_FORMS": "3",  
            "form-INITIAL_FORMS": "0",  
            "form-MAX_NUM_FORMS": "",  
            "form-0-name": "Charles Baudelaire",
            "form-1-name": "Arthur Rimbaud",
            "form-2-name": "",
        }

        formset = AuthorFormSet(data=data, queryset=qs)
        self.assertTrue(formset.is_valid())

        saved = formset.save()
        self.assertEqual(len(saved), 2)
        author1, author2 = saved
        self.assertEqual(author1, Author.objects.get(name="Charles Baudelaire"))
        self.assertEqual(author2, Author.objects.get(name="Arthur Rimbaud"))

        authors = list(Author.objects.order_by("name"))
        self.assertEqual(authors, [author2, author1])

        
        
        
        

        qs = Author.objects.order_by("name")
        AuthorFormSet = modelformset_factory(
            Author, fields="__all__", extra=1, can_delete=False
        )

        formset = AuthorFormSet(queryset=qs)
        self.assertEqual(len(formset.forms), 3)
        self.assertHTMLEqual(
            formset.forms[0].as_p(),
            '<p><label for="id_form-0-name">Name:</label>'
            '<input id="id_form-0-name" type="text" name="form-0-name" '
            'value="Arthur Rimbaud" maxlength="100">'
            '<input type="hidden" name="form-0-id" value="%d" id="id_form-0-id"></p>'
            % author2.id,
        )
        self.assertHTMLEqual(
            formset.forms[1].as_p(),
            '<p><label for="id_form-1-name">Name:</label>'
            '<input id="id_form-1-name" type="text" name="form-1-name" '
            'value="Charles Baudelaire" maxlength="100">'
            '<input type="hidden" name="form-1-id" value="%d" id="id_form-1-id"></p>'
            % author1.id,
        )
        self.assertHTMLEqual(
            formset.forms[2].as_p(),
            '<p><label for="id_form-2-name">Name:</label>'
            '<input id="id_form-2-name" type="text" name="form-2-name" maxlength="100">'
            '<input type="hidden" name="form-2-id" id="id_form-2-id"></p>',
        )

        data = {
            "form-TOTAL_FORMS": "3",  
            "form-INITIAL_FORMS": "2",  
            "form-MAX_NUM_FORMS": "",  
            "form-0-id": str(author2.id),
            "form-0-name": "Arthur Rimbaud",
            "form-1-id": str(author1.id),
            "form-1-name": "Charles Baudelaire",
            "form-2-name": "Paul Verlaine",
        }

        formset = AuthorFormSet(data=data, queryset=qs)
        self.assertTrue(formset.is_valid())

        
        saved = formset.save()
        self.assertEqual(len(saved), 1)
        author3 = saved[0]
        self.assertEqual(author3, Author.objects.get(name="Paul Verlaine"))

        authors = list(Author.objects.order_by("name"))
        self.assertEqual(authors, [author2, author1, author3])

        
        

        qs = Author.objects.order_by("name")
        AuthorFormSet = modelformset_factory(
            Author, fields="__all__", extra=1, can_delete=True
        )

        formset = AuthorFormSet(queryset=qs)
        self.assertEqual(len(formset.forms), 4)
        self.assertHTMLEqual(
            formset.forms[0].as_p(),
            '<p><label for="id_form-0-name">Name:</label>'
            '<input id="id_form-0-name" type="text" name="form-0-name" '
            'value="Arthur Rimbaud" maxlength="100"></p>'
            '<p><label for="id_form-0-DELETE">Delete:</label>'
            '<input type="checkbox" name="form-0-DELETE" id="id_form-0-DELETE">'
            '<input type="hidden" name="form-0-id" value="%d" id="id_form-0-id"></p>'
            % author2.id,
        )
        self.assertHTMLEqual(
            formset.forms[1].as_p(),
            '<p><label for="id_form-1-name">Name:</label>'
            '<input id="id_form-1-name" type="text" name="form-1-name" '
            'value="Charles Baudelaire" maxlength="100"></p>'
            '<p><label for="id_form-1-DELETE">Delete:</label>'
            '<input type="checkbox" name="form-1-DELETE" id="id_form-1-DELETE">'
            '<input type="hidden" name="form-1-id" value="%d" id="id_form-1-id"></p>'
            % author1.id,
        )
        self.assertHTMLEqual(
            formset.forms[2].as_p(),
            '<p><label for="id_form-2-name">Name:</label>'
            '<input id="id_form-2-name" type="text" name="form-2-name" '
            'value="Paul Verlaine" maxlength="100"></p>'
            '<p><label for="id_form-2-DELETE">Delete:</label>'
            '<input type="checkbox" name="form-2-DELETE" id="id_form-2-DELETE">'
            '<input type="hidden" name="form-2-id" value="%d" id="id_form-2-id"></p>'
            % author3.id,
        )
        self.assertHTMLEqual(
            formset.forms[3].as_p(),
            '<p><label for="id_form-3-name">Name:</label>'
            '<input id="id_form-3-name" type="text" name="form-3-name" maxlength="100">'
            '</p><p><label for="id_form-3-DELETE">Delete:</label>'
            '<input type="checkbox" name="form-3-DELETE" id="id_form-3-DELETE">'
            '<input type="hidden" name="form-3-id" id="id_form-3-id"></p>',
        )

        data = {
            "form-TOTAL_FORMS": "4",  
            "form-INITIAL_FORMS": "3",  
            "form-MAX_NUM_FORMS": "",  
            "form-0-id": str(author2.id),
            "form-0-name": "Arthur Rimbaud",
            "form-1-id": str(author1.id),
            "form-1-name": "Charles Baudelaire",
            "form-2-id": str(author3.id),
            "form-2-name": "Paul Verlaine",
            "form-3-name": "Walt Whitman",
            "form-3-DELETE": "on",
        }

        formset = AuthorFormSet(data=data, queryset=qs)
        self.assertTrue(formset.is_valid())

        

        self.assertEqual(formset.save(), [])

        authors = list(Author.objects.order_by("name"))
        self.assertEqual(authors, [author2, author1, author3])

        

        data = {
            "form-TOTAL_FORMS": "4",  
            "form-INITIAL_FORMS": "3",  
            "form-MAX_NUM_FORMS": "",  
            "form-0-id": str(author2.id),
            "form-0-name": "Walt Whitman",
            "form-1-id": str(author1.id),
            "form-1-name": "Charles Baudelaire",
            "form-2-id": str(author3.id),
            "form-2-name": "Paul Verlaine",
            "form-3-name": "",
            "form-3-DELETE": "",
        }

        formset = AuthorFormSet(data=data, queryset=qs)
        self.assertTrue(formset.is_valid())

        

        saved = formset.save()
        self.assertEqual(len(saved), 1)
        self.assertEqual(saved[0], Author.objects.get(name="Walt Whitman"))

    def test_commit_false(self):
        

        author1 = Author.objects.create(name="Charles Baudelaire")
        author2 = Author.objects.create(name="Paul Verlaine")
        author3 = Author.objects.create(name="Walt Whitman")

        meeting = AuthorMeeting.objects.create(created=date.today())
        meeting.authors.set(Author.objects.all())

        

        author4 = Author.objects.create(name="John Steinbeck")

        AuthorMeetingFormSet = modelformset_factory(
            AuthorMeeting, fields="__all__", extra=1, can_delete=True
        )
        data = {
            "form-TOTAL_FORMS": "2",  
            "form-INITIAL_FORMS": "1",  
            "form-MAX_NUM_FORMS": "",  
            "form-0-id": str(meeting.id),
            "form-0-name": "2nd Tuesday of the Week Meeting",
            "form-0-authors": [author2.id, author1.id, author3.id, author4.id],
            "form-1-name": "",
            "form-1-authors": "",
            "form-1-DELETE": "",
        }
        formset = AuthorMeetingFormSet(data=data, queryset=AuthorMeeting.objects.all())
        self.assertTrue(formset.is_valid())

        instances = formset.save(commit=False)
        for instance in instances:
            instance.created = date.today()
            instance.save()
        formset.save_m2m()
        self.assertSequenceEqual(
            instances[0].authors.all(),
            [author1, author4, author2, author3],
        )

    def test_max_num(self):
        
        
        

        a1 = Author.objects.create(name="Charles Baudelaire")
        a2 = Author.objects.create(name="Paul Verlaine")
        a3 = Author.objects.create(name="Walt Whitman")

        qs = Author.objects.order_by("name")

        AuthorFormSet = modelformset_factory(
            Author, fields="__all__", max_num=None, extra=3
        )
        formset = AuthorFormSet(queryset=qs)
        self.assertEqual(len(formset.forms), 6)
        self.assertEqual(len(formset.extra_forms), 3)

        AuthorFormSet = modelformset_factory(
            Author, fields="__all__", max_num=4, extra=3
        )
        formset = AuthorFormSet(queryset=qs)
        self.assertEqual(len(formset.forms), 4)
        self.assertEqual(len(formset.extra_forms), 1)

        AuthorFormSet = modelformset_factory(
            Author, fields="__all__", max_num=0, extra=3
        )
        formset = AuthorFormSet(queryset=qs)
        self.assertEqual(len(formset.forms), 3)
        self.assertEqual(len(formset.extra_forms), 0)

        AuthorFormSet = modelformset_factory(Author, fields="__all__", max_num=None)
        formset = AuthorFormSet(queryset=qs)
        self.assertSequenceEqual(formset.get_queryset(), [a1, a2, a3])

        AuthorFormSet = modelformset_factory(Author, fields="__all__", max_num=0)
        formset = AuthorFormSet(queryset=qs)
        self.assertSequenceEqual(formset.get_queryset(), [a1, a2, a3])

        AuthorFormSet = modelformset_factory(Author, fields="__all__", max_num=4)
        formset = AuthorFormSet(queryset=qs)
        self.assertSequenceEqual(formset.get_queryset(), [a1, a2, a3])

    def test_min_num(self):
        
        
        qs = Author.objects.none()

        AuthorFormSet = modelformset_factory(Author, fields="__all__", extra=0)
        formset = AuthorFormSet(queryset=qs)
        self.assertEqual(len(formset.forms), 0)

        AuthorFormSet = modelformset_factory(
            Author, fields="__all__", min_num=1, extra=0
        )
        formset = AuthorFormSet(queryset=qs)
        self.assertEqual(len(formset.forms), 1)

        AuthorFormSet = modelformset_factory(
            Author, fields="__all__", min_num=1, extra=1
        )
        formset = AuthorFormSet(queryset=qs)
        self.assertEqual(len(formset.forms), 2)

    def test_min_num_with_existing(self):
        
        Author.objects.create(name="Charles Baudelaire")
        qs = Author.objects.all()

        AuthorFormSet = modelformset_factory(
            Author, fields="__all__", extra=0, min_num=1
        )
        formset = AuthorFormSet(queryset=qs)
        self.assertEqual(len(formset.forms), 1)

    def test_custom_save_method(self):
        class PoetForm(forms.ModelForm):
            def save(self, commit=True):
                
                author = super().save(commit=False)
                author.name = "Vladimir Mayakovsky"
                if commit:
                    author.save()
                return author

        PoetFormSet = modelformset_factory(Poet, fields="__all__", form=PoetForm)

        data = {
            "form-TOTAL_FORMS": "3",  
            "form-INITIAL_FORMS": "0",  
            "form-MAX_NUM_FORMS": "",  
            "form-0-name": "Walt Whitman",
            "form-1-name": "Charles Baudelaire",
            "form-2-name": "",
        }

        qs = Poet.objects.all()
        formset = PoetFormSet(data=data, queryset=qs)
        self.assertTrue(formset.is_valid())

        poets = formset.save()
        self.assertEqual(len(poets), 2)
        poet1, poet2 = poets
        self.assertEqual(poet1.name, "Vladimir Mayakovsky")
        self.assertEqual(poet2.name, "Vladimir Mayakovsky")

    def test_custom_form(self):
        

        class PostForm1(forms.ModelForm):
            class Meta:
                model = Post
                fields = ("title", "posted")

        class PostForm2(forms.ModelForm):
            class Meta:
                model = Post
                exclude = ("subtitle",)

        PostFormSet = modelformset_factory(Post, form=PostForm1)
        formset = PostFormSet()
        self.assertNotIn("subtitle", formset.forms[0].fields)

        PostFormSet = modelformset_factory(Post, form=PostForm2)
        formset = PostFormSet()
        self.assertNotIn("subtitle", formset.forms[0].fields)

    def test_custom_queryset_init(self):
        
        Author.objects.create(name="Charles Baudelaire")
        Author.objects.create(name="Paul Verlaine")

        class BaseAuthorFormSet(BaseModelFormSet):
            def __init__(self, *args, **kwargs):
                super().__init__(*args, **kwargs)
                self.queryset = Author.objects.filter(name__startswith="Charles")

        AuthorFormSet = modelformset_factory(
            Author, fields="__all__", formset=BaseAuthorFormSet
        )
        formset = AuthorFormSet()
        self.assertEqual(len(formset.get_queryset()), 1)

    def test_model_inheritance(self):
        BetterAuthorFormSet = modelformset_factory(BetterAuthor, fields="__all__")
        formset = BetterAuthorFormSet()
        self.assertEqual(len(formset.forms), 1)
        self.assertHTMLEqual(
            formset.forms[0].as_p(),
            '<p><label for="id_form-0-name">Name:</label>'
            '<input id="id_form-0-name" type="text" name="form-0-name" maxlength="100">'
            '</p><p><label for="id_form-0-write_speed">Write speed:</label>'
            '<input type="number" name="form-0-write_speed" id="id_form-0-write_speed">'
            '<input type="hidden" name="form-0-author_ptr" id="id_form-0-author_ptr">'
            "</p>",
        )

        data = {
            "form-TOTAL_FORMS": "1",  
            "form-INITIAL_FORMS": "0",  
            "form-MAX_NUM_FORMS": "",  
            "form-0-author_ptr": "",
            "form-0-name": "Ernest Hemingway",
            "form-0-write_speed": "10",
        }

        formset = BetterAuthorFormSet(data)
        self.assertTrue(formset.is_valid())
        saved = formset.save()
        self.assertEqual(len(saved), 1)
        (author1,) = saved
        self.assertEqual(author1, BetterAuthor.objects.get(name="Ernest Hemingway"))
        hemingway_id = BetterAuthor.objects.get(name="Ernest Hemingway").pk

        formset = BetterAuthorFormSet()
        self.assertEqual(len(formset.forms), 2)
        self.assertHTMLEqual(
            formset.forms[0].as_p(),
            '<p><label for="id_form-0-name">Name:</label>'
            '<input id="id_form-0-name" type="text" name="form-0-name" '
            'value="Ernest Hemingway" maxlength="100"></p>'
            '<p><label for="id_form-0-write_speed">Write speed:</label>'
            '<input type="number" name="form-0-write_speed" value="10" '
            'id="id_form-0-write_speed">'
            '<input type="hidden" name="form-0-author_ptr" value="%d" '
            'id="id_form-0-author_ptr"></p>' % hemingway_id,
        )
        self.assertHTMLEqual(
            formset.forms[1].as_p(),
            '<p><label for="id_form-1-name">Name:</label>'
            '<input id="id_form-1-name" type="text" name="form-1-name" maxlength="100">'
            '</p><p><label for="id_form-1-write_speed">Write speed:</label>'
            '<input type="number" name="form-1-write_speed" id="id_form-1-write_speed">'
            '<input type="hidden" name="form-1-author_ptr" id="id_form-1-author_ptr">'
            "</p>",
        )

        data = {
            "form-TOTAL_FORMS": "2",  
            "form-INITIAL_FORMS": "1",  
            "form-MAX_NUM_FORMS": "",  
            "form-0-author_ptr": hemingway_id,
            "form-0-name": "Ernest Hemingway",
            "form-0-write_speed": "10",
            "form-1-author_ptr": "",
            "form-1-name": "",
            "form-1-write_speed": "",
        }

        formset = BetterAuthorFormSet(data)
        self.assertTrue(formset.is_valid())
        self.assertEqual(formset.save(), [])

    def test_inline_formsets(self):
        
        

        AuthorBooksFormSet = inlineformset_factory(
            Author, Book, can_delete=False, extra=3, fields="__all__"
        )
        author = Author.objects.create(name="Charles Baudelaire")

        formset = AuthorBooksFormSet(instance=author)
        self.assertEqual(len(formset.forms), 3)
        self.assertHTMLEqual(
            formset.forms[0].as_p(),
            '<p><label for="id_book_set-0-title">Title:</label>'
            '<input id="id_book_set-0-title" type="text" name="book_set-0-title" '
            'maxlength="100">'
            '<input type="hidden" name="book_set-0-author" value="%d" '
            'id="id_book_set-0-author">'
            '<input type="hidden" name="book_set-0-id" id="id_book_set-0-id">'
            "</p>" % author.id,
        )
        self.assertHTMLEqual(
            formset.forms[1].as_p(),
            '<p><label for="id_book_set-1-title">Title:</label>'
            '<input id="id_book_set-1-title" type="text" name="book_set-1-title" '
            'maxlength="100">'
            '<input type="hidden" name="book_set-1-author" value="%d" '
            'id="id_book_set-1-author">'
            '<input type="hidden" name="book_set-1-id" id="id_book_set-1-id"></p>'
            % author.id,
        )
        self.assertHTMLEqual(
            formset.forms[2].as_p(),
            '<p><label for="id_book_set-2-title">Title:</label>'
            '<input id="id_book_set-2-title" type="text" name="book_set-2-title" '
            'maxlength="100">'
            '<input type="hidden" name="book_set-2-author" value="%d" '
            'id="id_book_set-2-author">'
            '<input type="hidden" name="book_set-2-id" id="id_book_set-2-id"></p>'
            % author.id,
        )

        data = {
            "book_set-TOTAL_FORMS": "3",  
            "book_set-INITIAL_FORMS": "0",  
            "book_set-MAX_NUM_FORMS": "",  
            "book_set-0-title": "Les Fleurs du Mal",
            "book_set-1-title": "",
            "book_set-2-title": "",
        }

        formset = AuthorBooksFormSet(data, instance=author)
        self.assertTrue(formset.is_valid())

        saved = formset.save()
        self.assertEqual(len(saved), 1)
        (book1,) = saved
        self.assertEqual(book1, Book.objects.get(title="Les Fleurs du Mal"))
        self.assertSequenceEqual(author.book_set.all(), [book1])

        
        
        

        AuthorBooksFormSet = inlineformset_factory(
            Author, Book, can_delete=False, extra=2, fields="__all__"
        )
        author = Author.objects.get(name="Charles Baudelaire")

        formset = AuthorBooksFormSet(instance=author)
        self.assertEqual(len(formset.forms), 3)
        self.assertHTMLEqual(
            formset.forms[0].as_p(),
            '<p><label for="id_book_set-0-title">Title:</label>'
            '<input id="id_book_set-0-title" type="text" name="book_set-0-title" '
            'value="Les Fleurs du Mal" maxlength="100">'
            '<input type="hidden" name="book_set-0-author" value="%d" '
            'id="id_book_set-0-author">'
            '<input type="hidden" name="book_set-0-id" value="%d" '
            'id="id_book_set-0-id"></p>'
            % (
                author.id,
                book1.id,
            ),
        )
        self.assertHTMLEqual(
            formset.forms[1].as_p(),
            '<p><label for="id_book_set-1-title">Title:</label>'
            '<input id="id_book_set-1-title" type="text" name="book_set-1-title" '
            'maxlength="100">'
            '<input type="hidden" name="book_set-1-author" value="%d" '
            'id="id_book_set-1-author">'
            '<input type="hidden" name="book_set-1-id" id="id_book_set-1-id"></p>'
            % author.id,
        )
        self.assertHTMLEqual(
            formset.forms[2].as_p(),
            '<p><label for="id_book_set-2-title">Title:</label>'
            '<input id="id_book_set-2-title" type="text" name="book_set-2-title" '
            'maxlength="100">'
            '<input type="hidden" name="book_set-2-author" value="%d" '
            'id="id_book_set-2-author">'
            '<input type="hidden" name="book_set-2-id" id="id_book_set-2-id"></p>'
            % author.id,
        )

        data = {
            "book_set-TOTAL_FORMS": "3",  
            "book_set-INITIAL_FORMS": "1",  
            "book_set-MAX_NUM_FORMS": "",  
            "book_set-0-id": str(book1.id),
            "book_set-0-title": "Les Fleurs du Mal",
            "book_set-1-title": "Les Paradis Artificiels",
            "book_set-2-title": "",
        }

        formset = AuthorBooksFormSet(data, instance=author)
        self.assertTrue(formset.is_valid())

        saved = formset.save()
        self.assertEqual(len(saved), 1)
        (book2,) = saved
        self.assertEqual(book2, Book.objects.get(title="Les Paradis Artificiels"))

        
        
        self.assertSequenceEqual(author.book_set.order_by("title"), [book1, book2])

    def test_inline_formsets_save_as_new(self):
        
        
        AuthorBooksFormSet = inlineformset_factory(
            Author, Book, can_delete=False, extra=2, fields="__all__"
        )
        Author.objects.create(name="Charles Baudelaire")

        
        data = QueryDict(mutable=True)
        data.update(
            {
                "book_set-TOTAL_FORMS": "3",  
                "book_set-INITIAL_FORMS": "2",  
                "book_set-MAX_NUM_FORMS": "",  
                "book_set-0-id": "1",
                "book_set-0-title": "Les Fleurs du Mal",
                "book_set-1-id": "2",
                "book_set-1-title": "Les Paradis Artificiels",
                "book_set-2-title": "",
            }
        )
        data._mutable = False

        formset = AuthorBooksFormSet(data, instance=Author(), save_as_new=True)
        self.assertTrue(formset.is_valid())
        self.assertIs(data._mutable, False)

        new_author = Author.objects.create(name="Charles Baudelaire")
        formset = AuthorBooksFormSet(data, instance=new_author, save_as_new=True)
        saved = formset.save()
        self.assertEqual(len(saved), 2)
        book1, book2 = saved
        self.assertEqual(book1.title, "Les Fleurs du Mal")
        self.assertEqual(book2.title, "Les Paradis Artificiels")

        

        formset = AuthorBooksFormSet(prefix="test")
        self.assertEqual(len(formset.forms), 2)
        self.assertHTMLEqual(
            formset.forms[0].as_p(),
            '<p><label for="id_test-0-title">Title:</label>'
            '<input id="id_test-0-title" type="text" name="test-0-title" '
            'maxlength="100">'
            '<input type="hidden" name="test-0-author" id="id_test-0-author">'
            '<input type="hidden" name="test-0-id" id="id_test-0-id"></p>',
        )

        self.assertHTMLEqual(
            formset.forms[1].as_p(),
            '<p><label for="id_test-1-title">Title:</label>'
            '<input id="id_test-1-title" type="text" name="test-1-title" '
            'maxlength="100">'
            '<input type="hidden" name="test-1-author" id="id_test-1-author">'
            '<input type="hidden" name="test-1-id" id="id_test-1-id"></p>',
        )

    def test_inline_formsets_with_custom_pk(self):
        
        
        self.maxDiff = 1024

        AuthorBooksFormSet2 = inlineformset_factory(
            Author, BookWithCustomPK, can_delete=False, extra=1, fields="__all__"
        )
        author = Author.objects.create(pk=1, name="Charles Baudelaire")

        formset = AuthorBooksFormSet2(instance=author)
        self.assertEqual(len(formset.forms), 1)
        self.assertHTMLEqual(
            formset.forms[0].as_p(),
            '<p><label for="id_bookwithcustompk_set-0-my_pk">My pk:</label>'
            '<input id="id_bookwithcustompk_set-0-my_pk" type="number" '
            'name="bookwithcustompk_set-0-my_pk" step="1"></p>'
            '<p><label for="id_bookwithcustompk_set-0-title">Title:</label>'
            '<input id="id_bookwithcustompk_set-0-title" type="text" '
            'name="bookwithcustompk_set-0-title" maxlength="100">'
            '<input type="hidden" name="bookwithcustompk_set-0-author" '
            'value="1" id="id_bookwithcustompk_set-0-author"></p>',
        )

        data = {
            
            "bookwithcustompk_set-TOTAL_FORMS": "1",
            
            "bookwithcustompk_set-INITIAL_FORMS": "0",
            
            "bookwithcustompk_set-MAX_NUM_FORMS": "",
            "bookwithcustompk_set-0-my_pk": "77777",
            "bookwithcustompk_set-0-title": "Les Fleurs du Mal",
        }

        formset = AuthorBooksFormSet2(data, instance=author)
        self.assertTrue(formset.is_valid())

        saved = formset.save()
        self.assertEqual(len(saved), 1)
        (book1,) = saved
        self.assertEqual(book1.pk, 77777)

        book1 = author.bookwithcustompk_set.get()
        self.assertEqual(book1.title, "Les Fleurs du Mal")

    def test_inline_formsets_with_multi_table_inheritance(self):
        
        

        AuthorBooksFormSet3 = inlineformset_factory(
            Author, AlternateBook, can_delete=False, extra=1, fields="__all__"
        )
        author = Author.objects.create(pk=1, name="Charles Baudelaire")

        formset = AuthorBooksFormSet3(instance=author)
        self.assertEqual(len(formset.forms), 1)
        self.assertHTMLEqual(
            formset.forms[0].as_p(),
            '<p><label for="id_alternatebook_set-0-title">Title:</label>'
            '<input id="id_alternatebook_set-0-title" type="text" '
            'name="alternatebook_set-0-title" maxlength="100"></p>'
            '<p><label for="id_alternatebook_set-0-notes">Notes:</label>'
            '<input id="id_alternatebook_set-0-notes" type="text" '
            'name="alternatebook_set-0-notes" maxlength="100">'
            '<input type="hidden" name="alternatebook_set-0-author" value="1" '
            'id="id_alternatebook_set-0-author">'
            '<input type="hidden" name="alternatebook_set-0-book_ptr" '
            'id="id_alternatebook_set-0-book_ptr"></p>',
        )

        data = {
            
            "alternatebook_set-TOTAL_FORMS": "1",
            
            "alternatebook_set-INITIAL_FORMS": "0",
            
            "alternatebook_set-MAX_NUM_FORMS": "",
            "alternatebook_set-0-title": "Flowers of Evil",
            "alternatebook_set-0-notes": "English translation of Les Fleurs du Mal",
        }

        formset = AuthorBooksFormSet3(data, instance=author)
        self.assertTrue(formset.is_valid())

        saved = formset.save()
        self.assertEqual(len(saved), 1)
        (book1,) = saved
        self.assertEqual(book1.title, "Flowers of Evil")
        self.assertEqual(book1.notes, "English translation of Les Fleurs du Mal")

    @skipUnlessDBFeature("supports_partially_nullable_unique_constraints")
    def test_inline_formsets_with_nullable_unique_together(self):
        
        

        AuthorBooksFormSet4 = inlineformset_factory(
            Author,
            BookWithOptionalAltEditor,
            can_delete=False,
            extra=2,
            fields="__all__",
        )
        author = Author.objects.create(pk=1, name="Charles Baudelaire")

        data = {
            
            "bookwithoptionalalteditor_set-TOTAL_FORMS": "2",
            
            "bookwithoptionalalteditor_set-INITIAL_FORMS": "0",
            
            "bookwithoptionalalteditor_set-MAX_NUM_FORMS": "",
            "bookwithoptionalalteditor_set-0-author": "1",
            "bookwithoptionalalteditor_set-0-title": "Les Fleurs du Mal",
            "bookwithoptionalalteditor_set-1-author": "1",
            "bookwithoptionalalteditor_set-1-title": "Les Fleurs du Mal",
        }
        formset = AuthorBooksFormSet4(data, instance=author)
        self.assertTrue(formset.is_valid())

        saved = formset.save()
        self.assertEqual(len(saved), 2)
        book1, book2 = saved
        self.assertEqual(book1.author_id, 1)
        self.assertEqual(book1.title, "Les Fleurs du Mal")
        self.assertEqual(book2.author_id, 1)
        self.assertEqual(book2.title, "Les Fleurs du Mal")

    def test_inline_formsets_with_custom_save_method(self):
        AuthorBooksFormSet = inlineformset_factory(
            Author, Book, can_delete=False, extra=2, fields="__all__"
        )
        author = Author.objects.create(pk=1, name="Charles Baudelaire")
        book1 = Book.objects.create(
            pk=1, author=author, title="Les Paradis Artificiels"
        )
        book2 = Book.objects.create(pk=2, author=author, title="Les Fleurs du Mal")
        book3 = Book.objects.create(pk=3, author=author, title="Flowers of Evil")

        class PoemForm(forms.ModelForm):
            def save(self, commit=True):
                
                poem = super().save(commit=False)
                poem.name = "Brooklyn Bridge"
                if commit:
                    poem.save()
                return poem

        PoemFormSet = inlineformset_factory(Poet, Poem, form=PoemForm, fields="__all__")

        data = {
            "poem_set-TOTAL_FORMS": "3",  
            "poem_set-INITIAL_FORMS": "0",  
            "poem_set-MAX_NUM_FORMS": "",  
            "poem_set-0-name": "The Cloud in Trousers",
            "poem_set-1-name": "I",
            "poem_set-2-name": "",
        }

        poet = Poet.objects.create(name="Vladimir Mayakovsky")
        formset = PoemFormSet(data=data, instance=poet)
        self.assertTrue(formset.is_valid())

        saved = formset.save()
        self.assertEqual(len(saved), 2)
        poem1, poem2 = saved
        self.assertEqual(poem1.name, "Brooklyn Bridge")
        self.assertEqual(poem2.name, "Brooklyn Bridge")

        

        custom_qs = Book.objects.order_by("-title")
        formset = AuthorBooksFormSet(instance=author, queryset=custom_qs)
        self.assertEqual(len(formset.forms), 5)
        self.assertHTMLEqual(
            formset.forms[0].as_p(),
            '<p><label for="id_book_set-0-title">Title:</label>'
            '<input id="id_book_set-0-title" type="text" name="book_set-0-title" '
            'value="Les Paradis Artificiels" maxlength="100">'
            '<input type="hidden" name="book_set-0-author" value="1" '
            'id="id_book_set-0-author">'
            '<input type="hidden" name="book_set-0-id" value="1" id="id_book_set-0-id">'
            "</p>",
        )
        self.assertHTMLEqual(
            formset.forms[1].as_p(),
            '<p><label for="id_book_set-1-title">Title:</label>'
            '<input id="id_book_set-1-title" type="text" name="book_set-1-title" '
            'value="Les Fleurs du Mal" maxlength="100">'
            '<input type="hidden" name="book_set-1-author" value="1" '
            'id="id_book_set-1-author">'
            '<input type="hidden" name="book_set-1-id" value="2" id="id_book_set-1-id">'
            "</p>",
        )
        self.assertHTMLEqual(
            formset.forms[2].as_p(),
            '<p><label for="id_book_set-2-title">Title:</label>'
            '<input id="id_book_set-2-title" type="text" name="book_set-2-title" '
            'value="Flowers of Evil" maxlength="100">'
            '<input type="hidden" name="book_set-2-author" value="1" '
            'id="id_book_set-2-author">'
            '<input type="hidden" name="book_set-2-id" value="3" '
            'id="id_book_set-2-id"></p>',
        )
        self.assertHTMLEqual(
            formset.forms[3].as_p(),
            '<p><label for="id_book_set-3-title">Title:</label>'
            '<input id="id_book_set-3-title" type="text" name="book_set-3-title" '
            'maxlength="100">'
            '<input type="hidden" name="book_set-3-author" value="1" '
            'id="id_book_set-3-author">'
            '<input type="hidden" name="book_set-3-id" id="id_book_set-3-id"></p>',
        )
        self.assertHTMLEqual(
            formset.forms[4].as_p(),
            '<p><label for="id_book_set-4-title">Title:</label>'
            '<input id="id_book_set-4-title" type="text" name="book_set-4-title" '
            'maxlength="100">'
            '<input type="hidden" name="book_set-4-author" value="1" '
            'id="id_book_set-4-author">'
            '<input type="hidden" name="book_set-4-id" id="id_book_set-4-id"></p>',
        )

        data = {
            "book_set-TOTAL_FORMS": "5",  
            "book_set-INITIAL_FORMS": "3",  
            "book_set-MAX_NUM_FORMS": "",  
            "book_set-0-id": str(book1.id),
            "book_set-0-title": "Les Paradis Artificiels",
            "book_set-1-id": str(book2.id),
            "book_set-1-title": "Les Fleurs du Mal",
            "book_set-2-id": str(book3.id),
            "book_set-2-title": "Flowers of Evil",
            "book_set-3-title": "Revue des deux mondes",
            "book_set-4-title": "",
        }
        formset = AuthorBooksFormSet(data, instance=author, queryset=custom_qs)
        self.assertTrue(formset.is_valid())

        custom_qs = Book.objects.filter(title__startswith="F")
        formset = AuthorBooksFormSet(instance=author, queryset=custom_qs)
        self.assertHTMLEqual(
            formset.forms[0].as_p(),
            '<p><label for="id_book_set-0-title">Title:</label>'
            '<input id="id_book_set-0-title" type="text" name="book_set-0-title" '
            'value="Flowers of Evil" maxlength="100">'
            '<input type="hidden" name="book_set-0-author" value="1" '
            'id="id_book_set-0-author">'
            '<input type="hidden" name="book_set-0-id" value="3" '
            'id="id_book_set-0-id"></p>',
        )
        self.assertHTMLEqual(
            formset.forms[1].as_p(),
            '<p><label for="id_book_set-1-title">Title:</label>'
            '<input id="id_book_set-1-title" type="text" name="book_set-1-title" '
            'maxlength="100">'
            '<input type="hidden" name="book_set-1-author" value="1" '
            'id="id_book_set-1-author">'
            '<input type="hidden" name="book_set-1-id" id="id_book_set-1-id"></p>',
        )
        self.assertHTMLEqual(
            formset.forms[2].as_p(),
            '<p><label for="id_book_set-2-title">Title:</label>'
            '<input id="id_book_set-2-title" type="text" name="book_set-2-title" '
            'maxlength="100">'
            '<input type="hidden" name="book_set-2-author" value="1" '
            'id="id_book_set-2-author">'
            '<input type="hidden" name="book_set-2-id" id="id_book_set-2-id"></p>',
        )

        data = {
            "book_set-TOTAL_FORMS": "3",  
            "book_set-INITIAL_FORMS": "1",  
            "book_set-MAX_NUM_FORMS": "",  
            "book_set-0-id": str(book3.id),
            "book_set-0-title": "Flowers of Evil",
            "book_set-1-title": "Revue des deux mondes",
            "book_set-2-title": "",
        }
        formset = AuthorBooksFormSet(data, instance=author, queryset=custom_qs)
        self.assertTrue(formset.is_valid())

    def test_inline_formsets_with_custom_save_method_related_instance(self):
        

        class PoemForm2(forms.ModelForm):
            def save(self, commit=True):
                poem = super().save(commit=False)
                poem.name = "%s by %s" % (poem.name, poem.poet.name)
                if commit:
                    poem.save()
                return poem

        PoemFormSet = inlineformset_factory(
            Poet, Poem, form=PoemForm2, fields="__all__"
        )
        data = {
            "poem_set-TOTAL_FORMS": "1",
            "poem_set-INITIAL_FORMS": "0",
            "poem_set-MAX_NUM_FORMS": "",
            "poem_set-0-name": "Le Lac",
        }
        poet = Poet()
        formset = PoemFormSet(data=data, instance=poet)
        self.assertTrue(formset.is_valid())

        
        
        
        poet.name = "Lamartine"
        poet.save()
        poem = formset.save()[0]
        self.assertEqual(poem.name, "Le Lac by Lamartine")

    def test_inline_formsets_with_wrong_fk_name(self):
        
        message = "fk_name 'title' is not a ForeignKey to 'model_formsets.Author'."
        with self.assertRaisesMessage(ValueError, message):
            inlineformset_factory(Author, Book, fields="__all__", fk_name="title")

    def test_custom_pk(self):
        

        CustomPrimaryKeyFormSet = modelformset_factory(
            CustomPrimaryKey, fields="__all__"
        )
        formset = CustomPrimaryKeyFormSet()
        self.assertEqual(len(formset.forms), 1)
        self.assertHTMLEqual(
            formset.forms[0].as_p(),
            '<p><label for="id_form-0-my_pk">My pk:</label>'
            '<input id="id_form-0-my_pk" type="text" name="form-0-my_pk" '
            'maxlength="10"></p>'
            '<p><label for="id_form-0-some_field">Some field:</label>'
            '<input id="id_form-0-some_field" type="text" name="form-0-some_field" '
            'maxlength="100"></p>',
        )

        

        place = Place.objects.create(pk=1, name="Giordanos", city="Chicago")

        FormSet = inlineformset_factory(
            Place, Owner, extra=2, can_delete=False, fields="__all__"
        )
        formset = FormSet(instance=place)
        self.assertEqual(len(formset.forms), 2)
        self.assertHTMLEqual(
            formset.forms[0].as_p(),
            '<p><label for="id_owner_set-0-name">Name:</label>'
            '<input id="id_owner_set-0-name" type="text" name="owner_set-0-name" '
            'maxlength="100">'
            '<input type="hidden" name="owner_set-0-place" value="1" '
            'id="id_owner_set-0-place">'
            '<input type="hidden" name="owner_set-0-auto_id" '
            'id="id_owner_set-0-auto_id"></p>',
        )
        self.assertHTMLEqual(
            formset.forms[1].as_p(),
            '<p><label for="id_owner_set-1-name">Name:</label>'
            '<input id="id_owner_set-1-name" type="text" name="owner_set-1-name" '
            'maxlength="100">'
            '<input type="hidden" name="owner_set-1-place" value="1" '
            'id="id_owner_set-1-place">'
            '<input type="hidden" name="owner_set-1-auto_id" '
            'id="id_owner_set-1-auto_id"></p>',
        )

        data = {
            "owner_set-TOTAL_FORMS": "2",
            "owner_set-INITIAL_FORMS": "0",
            "owner_set-MAX_NUM_FORMS": "",
            "owner_set-0-auto_id": "",
            "owner_set-0-name": "Joe Perry",
            "owner_set-1-auto_id": "",
            "owner_set-1-name": "",
        }
        formset = FormSet(data, instance=place)
        self.assertTrue(formset.is_valid())
        saved = formset.save()
        self.assertEqual(len(saved), 1)
        (owner1,) = saved
        self.assertEqual(owner1.name, "Joe Perry")
        self.assertEqual(owner1.place.name, "Giordanos")

        formset = FormSet(instance=place)
        self.assertEqual(len(formset.forms), 3)
        self.assertHTMLEqual(
            formset.forms[0].as_p(),
            '<p><label for="id_owner_set-0-name">Name:</label>'
            '<input id="id_owner_set-0-name" type="text" name="owner_set-0-name" '
            'value="Joe Perry" maxlength="100">'
            '<input type="hidden" name="owner_set-0-place" value="1" '
            'id="id_owner_set-0-place">'
            '<input type="hidden" name="owner_set-0-auto_id" value="%d" '
            'id="id_owner_set-0-auto_id"></p>' % owner1.auto_id,
        )
        self.assertHTMLEqual(
            formset.forms[1].as_p(),
            '<p><label for="id_owner_set-1-name">Name:</label>'
            '<input id="id_owner_set-1-name" type="text" name="owner_set-1-name" '
            'maxlength="100">'
            '<input type="hidden" name="owner_set-1-place" value="1" '
            'id="id_owner_set-1-place">'
            '<input type="hidden" name="owner_set-1-auto_id" '
            'id="id_owner_set-1-auto_id"></p>',
        )
        self.assertHTMLEqual(
            formset.forms[2].as_p(),
            '<p><label for="id_owner_set-2-name">Name:</label>'
            '<input id="id_owner_set-2-name" type="text" name="owner_set-2-name" '
            'maxlength="100">'
            '<input type="hidden" name="owner_set-2-place" value="1" '
            'id="id_owner_set-2-place">'
            '<input type="hidden" name="owner_set-2-auto_id" '
            'id="id_owner_set-2-auto_id"></p>',
        )

        data = {
            "owner_set-TOTAL_FORMS": "3",
            "owner_set-INITIAL_FORMS": "1",
            "owner_set-MAX_NUM_FORMS": "",
            "owner_set-0-auto_id": str(owner1.auto_id),
            "owner_set-0-name": "Joe Perry",
            "owner_set-1-auto_id": "",
            "owner_set-1-name": "Jack Berry",
            "owner_set-2-auto_id": "",
            "owner_set-2-name": "",
        }
        formset = FormSet(data, instance=place)
        self.assertTrue(formset.is_valid())
        saved = formset.save()
        self.assertEqual(len(saved), 1)
        (owner2,) = saved
        self.assertEqual(owner2.name, "Jack Berry")
        self.assertEqual(owner2.place.name, "Giordanos")

        
        
        FormSet = modelformset_factory(OwnerProfile, fields="__all__")
        formset = FormSet()
        self.assertHTMLEqual(
            formset.forms[0].as_p(),
            '<p><label for="id_form-0-owner">Owner:</label>'
            '<select name="form-0-owner" id="id_form-0-owner">'
            '<option value="" selected>---------</option>'
            '<option value="%d">Joe Perry at Giordanos</option>'
            '<option value="%d">Jack Berry at Giordanos</option>'
            "</select></p>"
            '<p><label for="id_form-0-age">Age:</label>'
            '<input type="number" name="form-0-age" id="id_form-0-age" min="0"></p>'
            % (owner1.auto_id, owner2.auto_id),
        )

        owner1 = Owner.objects.get(name="Joe Perry")
        FormSet = inlineformset_factory(
            Owner, OwnerProfile, max_num=1, can_delete=False, fields="__all__"
        )
        self.assertEqual(FormSet.max_num, 1)

        formset = FormSet(instance=owner1)
        self.assertEqual(len(formset.forms), 1)
        self.assertHTMLEqual(
            formset.forms[0].as_p(),
            '<p><label for="id_ownerprofile-0-age">Age:</label>'
            '<input type="number" name="ownerprofile-0-age" '
            'id="id_ownerprofile-0-age" min="0">'
            '<input type="hidden" name="ownerprofile-0-owner" value="%d" '
            'id="id_ownerprofile-0-owner"></p>' % owner1.auto_id,
        )

        data = {
            "ownerprofile-TOTAL_FORMS": "1",
            "ownerprofile-INITIAL_FORMS": "0",
            "ownerprofile-MAX_NUM_FORMS": "1",
            "ownerprofile-0-owner": "",
            "ownerprofile-0-age": "54",
        }
        formset = FormSet(data, instance=owner1)
        self.assertTrue(formset.is_valid())
        saved = formset.save()
        self.assertEqual(len(saved), 1)
        (profile1,) = saved
        self.assertEqual(profile1.owner, owner1)
        self.assertEqual(profile1.age, 54)

        formset = FormSet(instance=owner1)
        self.assertEqual(len(formset.forms), 1)
        self.assertHTMLEqual(
            formset.forms[0].as_p(),
            '<p><label for="id_ownerprofile-0-age">Age:</label>'
            '<input type="number" name="ownerprofile-0-age" value="54" '
            'id="id_ownerprofile-0-age" min="0">'
            '<input type="hidden" name="ownerprofile-0-owner" value="%d" '
            'id="id_ownerprofile-0-owner"></p>' % owner1.auto_id,
        )

        data = {
            "ownerprofile-TOTAL_FORMS": "1",
            "ownerprofile-INITIAL_FORMS": "1",
            "ownerprofile-MAX_NUM_FORMS": "1",
            "ownerprofile-0-owner": str(owner1.auto_id),
            "ownerprofile-0-age": "55",
        }
        formset = FormSet(data, instance=owner1)
        self.assertTrue(formset.is_valid())
        saved = formset.save()
        self.assertEqual(len(saved), 1)
        (profile1,) = saved
        self.assertEqual(profile1.owner, owner1)
        self.assertEqual(profile1.age, 55)

    def test_unique_true_enforces_max_num_one(self):
        

        place = Place.objects.create(pk=1, name="Giordanos", city="Chicago")

        FormSet = inlineformset_factory(
            Place, Location, can_delete=False, fields="__all__"
        )
        self.assertEqual(FormSet.max_num, 1)

        formset = FormSet(instance=place)
        self.assertEqual(len(formset.forms), 1)
        self.assertHTMLEqual(
            formset.forms[0].as_p(),
            '<p><label for="id_location_set-0-lat">Lat:</label>'
            '<input id="id_location_set-0-lat" type="text" name="location_set-0-lat" '
            'maxlength="100"></p>'
            '<p><label for="id_location_set-0-lon">Lon:</label>'
            '<input id="id_location_set-0-lon" type="text" name="location_set-0-lon" '
            'maxlength="100">'
            '<input type="hidden" name="location_set-0-place" value="1" '
            'id="id_location_set-0-place">'
            '<input type="hidden" name="location_set-0-id" '
            'id="id_location_set-0-id"></p>',
        )

    def test_foreign_keys_in_parents(self):
        self.assertEqual(type(_get_foreign_key(Restaurant, Owner)), models.ForeignKey)
        self.assertEqual(
            type(_get_foreign_key(MexicanRestaurant, Owner)), models.ForeignKey
        )

    def test_unique_validation(self):
        FormSet = modelformset_factory(Product, fields="__all__", extra=1)
        data = {
            "form-TOTAL_FORMS": "1",
            "form-INITIAL_FORMS": "0",
            "form-MAX_NUM_FORMS": "",
            "form-0-slug": "car-red",
        }
        formset = FormSet(data)
        self.assertTrue(formset.is_valid())
        saved = formset.save()
        self.assertEqual(len(saved), 1)
        (product1,) = saved
        self.assertEqual(product1.slug, "car-red")

        data = {
            "form-TOTAL_FORMS": "1",
            "form-INITIAL_FORMS": "0",
            "form-MAX_NUM_FORMS": "",
            "form-0-slug": "car-red",
        }
        formset = FormSet(data)
        self.assertFalse(formset.is_valid())
        self.assertEqual(
            formset.errors, [{"slug": ["Product with this Slug already exists."]}]
        )

    def test_modelformset_validate_max_flag(self):
        
        
        
        

        data = {
            "form-TOTAL_FORMS": "2",
            "form-INITIAL_FORMS": "0",
            "form-MAX_NUM_FORMS": "2",  
            "form-0-price": "12.00",
            "form-0-quantity": "1",
            "form-1-price": "24.00",
            "form-1-quantity": "2",
        }

        FormSet = modelformset_factory(
            Price, fields="__all__", extra=1, max_num=1, validate_max=True
        )
        formset = FormSet(data)
        self.assertFalse(formset.is_valid())
        self.assertEqual(formset.non_form_errors(), ["Please submit at most 1 form."])

        
        
        FormSet = modelformset_factory(Price, fields="__all__", extra=1, max_num=1)
        formset = FormSet(data)
        self.assertTrue(formset.is_valid())

    def test_modelformset_min_num_equals_max_num_less_than(self):
        data = {
            "form-TOTAL_FORMS": "3",
            "form-INITIAL_FORMS": "0",
            "form-MAX_NUM_FORMS": "2",
            "form-0-slug": "car-red",
            "form-1-slug": "car-blue",
            "form-2-slug": "car-black",
        }
        FormSet = modelformset_factory(
            Product,
            fields="__all__",
            extra=1,
            max_num=2,
            validate_max=True,
            min_num=2,
            validate_min=True,
        )
        formset = FormSet(data)
        self.assertFalse(formset.is_valid())
        self.assertEqual(formset.non_form_errors(), ["Please submit at most 2 forms."])

    def test_modelformset_min_num_equals_max_num_more_than(self):
        data = {
            "form-TOTAL_FORMS": "1",
            "form-INITIAL_FORMS": "0",
            "form-MAX_NUM_FORMS": "2",
            "form-0-slug": "car-red",
        }
        FormSet = modelformset_factory(
            Product,
            fields="__all__",
            extra=1,
            max_num=2,
            validate_max=True,
            min_num=2,
            validate_min=True,
        )
        formset = FormSet(data)
        self.assertFalse(formset.is_valid())
        self.assertEqual(formset.non_form_errors(), ["Please submit at least 2 forms."])

    def test_unique_together_validation(self):
        FormSet = modelformset_factory(Price, fields="__all__", extra=1)
        data = {
            "form-TOTAL_FORMS": "1",
            "form-INITIAL_FORMS": "0",
            "form-MAX_NUM_FORMS": "",
            "form-0-price": "12.00",
            "form-0-quantity": "1",
        }
        formset = FormSet(data)
        self.assertTrue(formset.is_valid())
        saved = formset.save()
        self.assertEqual(len(saved), 1)
        (price1,) = saved
        self.assertEqual(price1.price, Decimal("12.00"))
        self.assertEqual(price1.quantity, 1)

        data = {
            "form-TOTAL_FORMS": "1",
            "form-INITIAL_FORMS": "0",
            "form-MAX_NUM_FORMS": "",
            "form-0-price": "12.00",
            "form-0-quantity": "1",
        }
        formset = FormSet(data)
        self.assertFalse(formset.is_valid())
        self.assertEqual(
            formset.errors,
            [{"__all__": ["Price with this Price and Quantity already exists."]}],
        )

    def test_unique_together_with_inlineformset_factory(self):
        

        repository = Repository.objects.create(name="Test Repo")
        FormSet = inlineformset_factory(Repository, Revision, extra=1, fields="__all__")
        data = {
            "revision_set-TOTAL_FORMS": "1",
            "revision_set-INITIAL_FORMS": "0",
            "revision_set-MAX_NUM_FORMS": "",
            "revision_set-0-repository": repository.pk,
            "revision_set-0-revision": "146239817507f148d448db38840db7c3cbf47c76",
            "revision_set-0-DELETE": "",
        }
        formset = FormSet(data, instance=repository)
        self.assertTrue(formset.is_valid())
        saved = formset.save()
        self.assertEqual(len(saved), 1)
        (revision1,) = saved
        self.assertEqual(revision1.repository, repository)
        self.assertEqual(revision1.revision, "146239817507f148d448db38840db7c3cbf47c76")

        
        data = {
            "revision_set-TOTAL_FORMS": "1",
            "revision_set-INITIAL_FORMS": "0",
            "revision_set-MAX_NUM_FORMS": "",
            "revision_set-0-repository": repository.pk,
            "revision_set-0-revision": "146239817507f148d448db38840db7c3cbf47c76",
            "revision_set-0-DELETE": "",
        }
        formset = FormSet(data, instance=repository)
        self.assertFalse(formset.is_valid())
        self.assertEqual(
            formset.errors,
            [
                {
                    "__all__": [
                        "Revision with this Repository and Revision already exists."
                    ]
                }
            ],
        )

        
        

        FormSet = inlineformset_factory(
            Repository, Revision, fields=("revision",), extra=1
        )
        data = {
            "revision_set-TOTAL_FORMS": "1",
            "revision_set-INITIAL_FORMS": "0",
            "revision_set-MAX_NUM_FORMS": "",
            "revision_set-0-repository": repository.pk,
            "revision_set-0-revision": "146239817507f148d448db38840db7c3cbf47c76",
            "revision_set-0-DELETE": "",
        }
        formset = FormSet(data, instance=repository)
        self.assertFalse(formset.is_valid())

    def test_callable_defaults(self):
        

        person = Person.objects.create(name="Ringo")
        FormSet = inlineformset_factory(
            Person, Membership, can_delete=False, extra=1, fields="__all__"
        )
        formset = FormSet(instance=person)

        
        
        

        self.assertEqual(len(formset.forms), 1)  
        form = formset.forms[0]
        now = form.fields["date_joined"].initial()
        result = form.as_p()
        result = re.sub(
            r"[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}(?:\.[0-9]+)?",
            "__DATETIME__",
            result,
        )
        self.assertHTMLEqual(
            result,
            '<p><label for="id_membership_set-0-date_joined">Date joined:</label>'
            '<input type="text" name="membership_set-0-date_joined" '
            'value="__DATETIME__" id="id_membership_set-0-date_joined">'
            '<input type="hidden" name="initial-membership_set-0-date_joined" '
            'value="__DATETIME__" '
            'id="initial-membership_set-0-id_membership_set-0-date_joined"></p>'
            '<p><label for="id_membership_set-0-karma">Karma:</label>'
            '<input type="number" name="membership_set-0-karma" '
            'id="id_membership_set-0-karma">'
            '<input type="hidden" name="membership_set-0-person" value="%d" '
            'id="id_membership_set-0-person">'
            '<input type="hidden" name="membership_set-0-id" '
            'id="id_membership_set-0-id"></p>' % person.id,
        )

        

        data = {
            "membership_set-TOTAL_FORMS": "1",
            "membership_set-INITIAL_FORMS": "0",
            "membership_set-MAX_NUM_FORMS": "",
            "membership_set-0-date_joined": now.strftime("%Y-%m-%d %H:%M:%S"),
            "initial-membership_set-0-date_joined": now.strftime("%Y-%m-%d %H:%M:%S"),
            "membership_set-0-karma": "",
        }
        formset = FormSet(data, instance=person)
        self.assertTrue(formset.is_valid())

        

        one_day_later = now + datetime.timedelta(days=1)
        filled_data = {
            "membership_set-TOTAL_FORMS": "1",
            "membership_set-INITIAL_FORMS": "0",
            "membership_set-MAX_NUM_FORMS": "",
            "membership_set-0-date_joined": one_day_later.strftime("%Y-%m-%d %H:%M:%S"),
            "initial-membership_set-0-date_joined": now.strftime("%Y-%m-%d %H:%M:%S"),
            "membership_set-0-karma": "",
        }
        formset = FormSet(filled_data, instance=person)
        self.assertFalse(formset.is_valid())

        

        class MembershipForm(forms.ModelForm):
            date_joined = forms.SplitDateTimeField(initial=now)

            class Meta:
                model = Membership
                fields = "__all__"

            def __init__(self, **kwargs):
                super().__init__(**kwargs)
                self.fields["date_joined"].widget = forms.SplitDateTimeWidget()

        FormSet = inlineformset_factory(
            Person,
            Membership,
            form=MembershipForm,
            can_delete=False,
            extra=1,
            fields="__all__",
        )
        data = {
            "membership_set-TOTAL_FORMS": "1",
            "membership_set-INITIAL_FORMS": "0",
            "membership_set-MAX_NUM_FORMS": "",
            "membership_set-0-date_joined_0": now.strftime("%Y-%m-%d"),
            "membership_set-0-date_joined_1": now.strftime("%H:%M:%S"),
            "initial-membership_set-0-date_joined": now.strftime("%Y-%m-%d %H:%M:%S"),
            "membership_set-0-karma": "",
        }
        formset = FormSet(data, instance=person)
        self.assertTrue(formset.is_valid())

    def test_inlineformset_factory_with_null_fk(self):
        
        
        team = Team.objects.create(name="Red Vipers")
        Player(name="Timmy").save()
        Player(name="Bobby", team=team).save()

        PlayerInlineFormSet = inlineformset_factory(Team, Player, fields="__all__")
        formset = PlayerInlineFormSet()
        self.assertQuerySetEqual(formset.get_queryset(), [])

        formset = PlayerInlineFormSet(instance=team)
        players = formset.get_queryset()
        self.assertEqual(len(players), 1)
        (player1,) = players
        self.assertEqual(player1.team, team)
        self.assertEqual(player1.name, "Bobby")

    def test_inlineformset_with_arrayfield(self):
        class SimpleArrayField(forms.CharField):
            

            def to_python(self, value):
                value = super().to_python(value)
                return value.split(",") if value else []

        class BookForm(forms.ModelForm):
            title = SimpleArrayField()

            class Meta:
                model = Book
                fields = ("title",)

        BookFormSet = inlineformset_factory(Author, Book, form=BookForm)
        data = {
            "book_set-TOTAL_FORMS": "3",
            "book_set-INITIAL_FORMS": "0",
            "book_set-MAX_NUM_FORMS": "",
            "book_set-0-title": "test1,test2",
            "book_set-1-title": "test1,test2",
            "book_set-2-title": "test3,test4",
        }
        author = Author.objects.create(name="test")
        formset = BookFormSet(data, instance=author)
        self.assertEqual(
            formset.errors,
            [{}, {"__all__": ["Please correct the duplicate values below."]}, {}],
        )

    def test_model_formset_with_custom_pk(self):
        
        
        FormSet = modelformset_factory(
            ClassyMexicanRestaurant, fields=["tacos_are_yummy"]
        )
        self.assertEqual(
            sorted(FormSet().forms[0].fields), ["tacos_are_yummy", "the_restaurant"]
        )

    def test_model_formset_with_initial_model_instance(self):
        
        
        FormSet = modelformset_factory(Poem, fields="__all__")
        john_milton = Poet(name="John Milton")
        john_milton.save()
        data = {
            "form-TOTAL_FORMS": 1,
            "form-INITIAL_FORMS": 0,
            "form-MAX_NUM_FORMS": "",
            "form-0-name": "",
            "form-0-poet": str(john_milton.id),
        }
        formset = FormSet(initial=[{"poet": john_milton}], data=data)
        self.assertFalse(formset.extra_forms[0].has_changed())

    def test_model_formset_with_initial_queryset(self):
        
        
        FormSet = modelformset_factory(AuthorMeeting, fields="__all__")
        Author.objects.create(pk=1, name="Charles Baudelaire")
        data = {
            "form-TOTAL_FORMS": 1,
            "form-INITIAL_FORMS": 0,
            "form-MAX_NUM_FORMS": "",
            "form-0-name": "",
            "form-0-created": "",
            "form-0-authors": list(Author.objects.values_list("id", flat=True)),
        }
        formset = FormSet(initial=[{"authors": Author.objects.all()}], data=data)
        self.assertFalse(formset.extra_forms[0].has_changed())

    def test_prevent_duplicates_from_with_the_same_formset(self):
        FormSet = modelformset_factory(Product, fields="__all__", extra=2)
        data = {
            "form-TOTAL_FORMS": 2,
            "form-INITIAL_FORMS": 0,
            "form-MAX_NUM_FORMS": "",
            "form-0-slug": "red_car",
            "form-1-slug": "red_car",
        }
        formset = FormSet(data)
        self.assertFalse(formset.is_valid())
        self.assertEqual(
            formset._non_form_errors, ["Please correct the duplicate data for slug."]
        )

        FormSet = modelformset_factory(Price, fields="__all__", extra=2)
        data = {
            "form-TOTAL_FORMS": 2,
            "form-INITIAL_FORMS": 0,
            "form-MAX_NUM_FORMS": "",
            "form-0-price": "25",
            "form-0-quantity": "7",
            "form-1-price": "25",
            "form-1-quantity": "7",
        }
        formset = FormSet(data)
        self.assertFalse(formset.is_valid())
        self.assertEqual(
            formset._non_form_errors,
            [
                "Please correct the duplicate data for price and quantity, which must "
                "be unique."
            ],
        )

        
        
        
        FormSet = modelformset_factory(Price, fields=("price",), extra=2)
        data = {
            "form-TOTAL_FORMS": "2",
            "form-INITIAL_FORMS": "0",
            "form-MAX_NUM_FORMS": "",
            "form-0-price": "24",
            "form-1-price": "24",
        }
        formset = FormSet(data)
        self.assertTrue(formset.is_valid())

        FormSet = inlineformset_factory(Author, Book, extra=0, fields="__all__")
        author = Author.objects.create(pk=1, name="Charles Baudelaire")
        Book.objects.create(pk=1, author=author, title="Les Paradis Artificiels")
        Book.objects.create(pk=2, author=author, title="Les Fleurs du Mal")
        Book.objects.create(pk=3, author=author, title="Flowers of Evil")

        book_ids = author.book_set.order_by("id").values_list("id", flat=True)
        data = {
            "book_set-TOTAL_FORMS": "2",
            "book_set-INITIAL_FORMS": "2",
            "book_set-MAX_NUM_FORMS": "",
            "book_set-0-title": "The 2008 Election",
            "book_set-0-author": str(author.id),
            "book_set-0-id": str(book_ids[0]),
            "book_set-1-title": "The 2008 Election",
            "book_set-1-author": str(author.id),
            "book_set-1-id": str(book_ids[1]),
        }
        formset = FormSet(data=data, instance=author)
        self.assertFalse(formset.is_valid())
        self.assertEqual(
            formset._non_form_errors, ["Please correct the duplicate data for title."]
        )
        self.assertEqual(
            formset.errors,
            [{}, {"__all__": ["Please correct the duplicate values below."]}],
        )

        FormSet = modelformset_factory(Post, fields="__all__", extra=2)
        data = {
            "form-TOTAL_FORMS": "2",
            "form-INITIAL_FORMS": "0",
            "form-MAX_NUM_FORMS": "",
            "form-0-title": "blah",
            "form-0-slug": "Morning",
            "form-0-subtitle": "foo",
            "form-0-posted": "2009-01-01",
            "form-1-title": "blah",
            "form-1-slug": "Morning in Prague",
            "form-1-subtitle": "rawr",
            "form-1-posted": "2009-01-01",
        }
        formset = FormSet(data)
        self.assertFalse(formset.is_valid())
        self.assertEqual(
            formset._non_form_errors,
            [
                "Please correct the duplicate data for title which must be unique for "
                "the date in posted."
            ],
        )
        self.assertEqual(
            formset.errors,
            [{}, {"__all__": ["Please correct the duplicate values below."]}],
        )

        data = {
            "form-TOTAL_FORMS": "2",
            "form-INITIAL_FORMS": "0",
            "form-MAX_NUM_FORMS": "",
            "form-0-title": "foo",
            "form-0-slug": "Morning in Prague",
            "form-0-subtitle": "foo",
            "form-0-posted": "2009-01-01",
            "form-1-title": "blah",
            "form-1-slug": "Morning in Prague",
            "form-1-subtitle": "rawr",
            "form-1-posted": "2009-08-02",
        }
        formset = FormSet(data)
        self.assertFalse(formset.is_valid())
        self.assertEqual(
            formset._non_form_errors,
            [
                "Please correct the duplicate data for slug which must be unique for "
                "the year in posted."
            ],
        )

        data = {
            "form-TOTAL_FORMS": "2",
            "form-INITIAL_FORMS": "0",
            "form-MAX_NUM_FORMS": "",
            "form-0-title": "foo",
            "form-0-slug": "Morning in Prague",
            "form-0-subtitle": "rawr",
            "form-0-posted": "2008-08-01",
            "form-1-title": "blah",
            "form-1-slug": "Prague",
            "form-1-subtitle": "rawr",
            "form-1-posted": "2009-08-02",
        }
        formset = FormSet(data)
        self.assertFalse(formset.is_valid())
        self.assertEqual(
            formset._non_form_errors,
            [
                "Please correct the duplicate data for subtitle which must be unique "
                "for the month in posted."
            ],
        )

    def test_prevent_change_outer_model_and_create_invalid_data(self):
        author = Author.objects.create(name="Charles")
        other_author = Author.objects.create(name="Walt")
        AuthorFormSet = modelformset_factory(Author, fields="__all__")
        data = {
            "form-TOTAL_FORMS": "2",
            "form-INITIAL_FORMS": "2",
            "form-MAX_NUM_FORMS": "",
            "form-0-id": str(author.id),
            "form-0-name": "Charles",
            "form-1-id": str(other_author.id),  
            "form-1-name": "Changed name",
        }
        
        
        formset = AuthorFormSet(
            data=data, queryset=Author.objects.filter(id__in=(author.id,))
        )
        self.assertTrue(formset.is_valid())
        formset.save()
        
        
        self.assertSequenceEqual(Author.objects.all(), [author, other_author])

    def test_validation_without_id(self):
        AuthorFormSet = modelformset_factory(Author, fields="__all__")
        data = {
            "form-TOTAL_FORMS": "1",
            "form-INITIAL_FORMS": "1",
            "form-MAX_NUM_FORMS": "",
            "form-0-name": "Charles",
        }
        formset = AuthorFormSet(data)
        self.assertEqual(
            formset.errors,
            [{"id": ["This field is required."]}],
        )

    def test_validation_with_child_model_without_id(self):
        BetterAuthorFormSet = modelformset_factory(BetterAuthor, fields="__all__")
        data = {
            "form-TOTAL_FORMS": "1",
            "form-INITIAL_FORMS": "1",
            "form-MAX_NUM_FORMS": "",
            "form-0-name": "Charles",
            "form-0-write_speed": "10",
        }
        formset = BetterAuthorFormSet(data)
        self.assertEqual(
            formset.errors,
            [{"author_ptr": ["This field is required."]}],
        )

    def test_validation_with_invalid_id(self):
        AuthorFormSet = modelformset_factory(Author, fields="__all__")
        data = {
            "form-TOTAL_FORMS": "1",
            "form-INITIAL_FORMS": "1",
            "form-MAX_NUM_FORMS": "",
            "form-0-id": "abc",
            "form-0-name": "Charles",
        }
        formset = AuthorFormSet(data)
        self.assertEqual(
            formset.errors,
            [
                {
                    "id": [
                        "Select a valid choice. That choice is not one of the "
                        "available choices."
                    ]
                }
            ],
        )

    def test_validation_with_nonexistent_id(self):
        AuthorFormSet = modelformset_factory(Author, fields="__all__")
        data = {
            "form-TOTAL_FORMS": "1",
            "form-INITIAL_FORMS": "1",
            "form-MAX_NUM_FORMS": "",
            "form-0-id": "12345",
            "form-0-name": "Charles",
        }
        formset = AuthorFormSet(data)
        self.assertEqual(
            formset.errors,
            [
                {
                    "id": [
                        "Select a valid choice. That choice is not one of the "
                        "available choices."
                    ]
                }
            ],
        )

    def test_initial_form_count_empty_data(self):
        AuthorFormSet = modelformset_factory(Author, fields="__all__")
        formset = AuthorFormSet({})
        self.assertEqual(formset.initial_form_count(), 0)

    def test_edit_only(self):
        charles = Author.objects.create(name="Charles Baudelaire")
        AuthorFormSet = modelformset_factory(Author, fields="__all__", edit_only=True)
        data = {
            "form-TOTAL_FORMS": "2",
            "form-INITIAL_FORMS": "0",
            "form-MAX_NUM_FORMS": "0",
            "form-0-name": "Arthur Rimbaud",
            "form-1-name": "Walt Whitman",
        }
        formset = AuthorFormSet(data)
        self.assertIs(formset.is_valid(), True)
        formset.save()
        self.assertSequenceEqual(Author.objects.all(), [charles])
        data = {
            "form-TOTAL_FORMS": "2",
            "form-INITIAL_FORMS": "1",
            "form-MAX_NUM_FORMS": "0",
            "form-0-id": charles.pk,
            "form-0-name": "Arthur Rimbaud",
            "form-1-name": "Walt Whitman",
        }
        formset = AuthorFormSet(data)
        self.assertIs(formset.is_valid(), True)
        formset.save()
        charles.refresh_from_db()
        self.assertEqual(charles.name, "Arthur Rimbaud")
        self.assertSequenceEqual(Author.objects.all(), [charles])

    def test_edit_only_inlineformset_factory(self):
        charles = Author.objects.create(name="Charles Baudelaire")
        book = Book.objects.create(author=charles, title="Les Paradis Artificiels")
        AuthorFormSet = inlineformset_factory(
            Author,
            Book,
            can_delete=False,
            fields="__all__",
            edit_only=True,
        )
        data = {
            "book_set-TOTAL_FORMS": "4",
            "book_set-INITIAL_FORMS": "1",
            "book_set-MAX_NUM_FORMS": "0",
            "book_set-0-id": book.pk,
            "book_set-0-title": "Les Fleurs du Mal",
            "book_set-0-author": charles.pk,
            "book_set-1-title": "Flowers of Evil",
            "book_set-1-author": charles.pk,
        }
        formset = AuthorFormSet(data, instance=charles)
        self.assertIs(formset.is_valid(), True)
        formset.save()
        book.refresh_from_db()
        self.assertEqual(book.title, "Les Fleurs du Mal")
        self.assertSequenceEqual(Book.objects.all(), [book])

    def test_edit_only_object_outside_of_queryset(self):
        charles = Author.objects.create(name="Charles Baudelaire")
        walt = Author.objects.create(name="Walt Whitman")
        data = {
            "form-TOTAL_FORMS": "1",
            "form-INITIAL_FORMS": "1",
            "form-0-id": walt.pk,
            "form-0-name": "Parth Patil",
        }
        AuthorFormSet = modelformset_factory(Author, fields="__all__", edit_only=True)
        formset = AuthorFormSet(data, queryset=Author.objects.filter(pk=charles.pk))
        self.assertIs(formset.is_valid(), True)
        formset.save()
        self.assertCountEqual(Author.objects.all(), [charles, walt])

    def test_edit_only_formset_factory_with_basemodelformset(self):
        charles = Author.objects.create(name="Charles Baudelaire")

        class AuthorForm(forms.ModelForm):
            class Meta:
                model = Author
                fields = "__all__"

        class BaseAuthorFormSet(BaseModelFormSet):
            def __init__(self, *args, **kwargs):
                self.model = Author
                super().__init__(*args, **kwargs)

        AuthorFormSet = formset_factory(AuthorForm, formset=BaseAuthorFormSet)
        data = {
            "form-TOTAL_FORMS": "2",
            "form-INITIAL_FORMS": "1",
            "form-MAX_NUM_FORMS": "0",
            "form-0-id": charles.pk,
            "form-0-name": "Shawn Dong",
            "form-1-name": "Walt Whitman",
        }
        formset = AuthorFormSet(data)
        self.assertIs(formset.is_valid(), True)
        formset.save()
        self.assertEqual(Author.objects.count(), 2)
        charles.refresh_from_db()
        self.assertEqual(charles.name, "Shawn Dong")
        self.assertEqual(Author.objects.count(), 2)


class TestModelFormsetOverridesTroughFormMeta(TestCase):
    def test_modelformset_factory_widgets(self):
        widgets = {"name": forms.TextInput(attrs={"class": "poet"})}
        PoetFormSet = modelformset_factory(Poet, fields="__all__", widgets=widgets)
        form = PoetFormSet.form()
        self.assertHTMLEqual(
            str(form["name"]),
            '<input id="id_name" maxlength="100" type="text" class="poet" name="name" '
            "required>",
        )

    def test_inlineformset_factory_widgets(self):
        widgets = {"title": forms.TextInput(attrs={"class": "book"})}
        BookFormSet = inlineformset_factory(
            Author, Book, widgets=widgets, fields="__all__"
        )
        form = BookFormSet.form()
        self.assertHTMLEqual(
            str(form["title"]),
            '<input class="book" id="id_title" maxlength="100" name="title" '
            'type="text" required>',
        )

    def test_modelformset_factory_labels_overrides(self):
        BookFormSet = modelformset_factory(
            Book, fields="__all__", labels={"title": "Name"}
        )
        form = BookFormSet.form()
        self.assertHTMLEqual(
            form["title"].label_tag(), '<label for="id_title">Name:</label>'
        )
        self.assertHTMLEqual(
            form["title"].legend_tag(),
            '<legend for="id_title">Name:</legend>',
        )

    def test_inlineformset_factory_labels_overrides(self):
        BookFormSet = inlineformset_factory(
            Author, Book, fields="__all__", labels={"title": "Name"}
        )
        form = BookFormSet.form()
        self.assertHTMLEqual(
            form["title"].label_tag(), '<label for="id_title">Name:</label>'
        )
        self.assertHTMLEqual(
            form["title"].legend_tag(),
            '<legend for="id_title">Name:</legend>',
        )

    def test_modelformset_factory_help_text_overrides(self):
        BookFormSet = modelformset_factory(
            Book, fields="__all__", help_texts={"title": "Choose carefully."}
        )
        form = BookFormSet.form()
        self.assertEqual(form["title"].help_text, "Choose carefully.")

    def test_inlineformset_factory_help_text_overrides(self):
        BookFormSet = inlineformset_factory(
            Author, Book, fields="__all__", help_texts={"title": "Choose carefully."}
        )
        form = BookFormSet.form()
        self.assertEqual(form["title"].help_text, "Choose carefully.")

    def test_modelformset_factory_error_messages_overrides(self):
        author = Author.objects.create(pk=1, name="Charles Baudelaire")
        BookFormSet = modelformset_factory(
            Book,
            fields="__all__",
            error_messages={"title": {"max_length": "Title too long!!"}},
        )
        form = BookFormSet.form(data={"title": "Foo " * 30, "author": author.id})
        form.full_clean()
        self.assertEqual(form.errors, {"title": ["Title too long!!"]})

    def test_inlineformset_factory_error_messages_overrides(self):
        author = Author.objects.create(pk=1, name="Charles Baudelaire")
        BookFormSet = inlineformset_factory(
            Author,
            Book,
            fields="__all__",
            error_messages={"title": {"max_length": "Title too long!!"}},
        )
        form = BookFormSet.form(data={"title": "Foo " * 30, "author": author.id})
        form.full_clean()
        self.assertEqual(form.errors, {"title": ["Title too long!!"]})

    def test_modelformset_factory_field_class_overrides(self):
        author = Author.objects.create(pk=1, name="Charles Baudelaire")
        BookFormSet = modelformset_factory(
            Book,
            fields="__all__",
            field_classes={
                "title": forms.SlugField,
            },
        )
        form = BookFormSet.form(data={"title": "Foo " * 30, "author": author.id})
        self.assertIs(Book._meta.get_field("title").__class__, models.CharField)
        self.assertIsInstance(form.fields["title"], forms.SlugField)

    def test_inlineformset_factory_field_class_overrides(self):
        author = Author.objects.create(pk=1, name="Charles Baudelaire")
        BookFormSet = inlineformset_factory(
            Author,
            Book,
            fields="__all__",
            field_classes={
                "title": forms.SlugField,
            },
        )
        form = BookFormSet.form(data={"title": "Foo " * 30, "author": author.id})
        self.assertIs(Book._meta.get_field("title").__class__, models.CharField)
        self.assertIsInstance(form.fields["title"], forms.SlugField)

    def test_modelformset_factory_absolute_max(self):
        AuthorFormSet = modelformset_factory(
            Author, fields="__all__", absolute_max=1500
        )
        data = {
            "form-TOTAL_FORMS": "1501",
            "form-INITIAL_FORMS": "0",
            "form-MAX_NUM_FORMS": "0",
        }
        formset = AuthorFormSet(data=data)
        self.assertIs(formset.is_valid(), False)
        self.assertEqual(len(formset.forms), 1500)
        self.assertEqual(
            formset.non_form_errors(),
            ["Please submit at most 1000 forms."],
        )

    def test_modelformset_factory_absolute_max_with_max_num(self):
        AuthorFormSet = modelformset_factory(
            Author,
            fields="__all__",
            max_num=20,
            absolute_max=100,
        )
        data = {
            "form-TOTAL_FORMS": "101",
            "form-INITIAL_FORMS": "0",
            "form-MAX_NUM_FORMS": "0",
        }
        formset = AuthorFormSet(data=data)
        self.assertIs(formset.is_valid(), False)
        self.assertEqual(len(formset.forms), 100)
        self.assertEqual(
            formset.non_form_errors(),
            ["Please submit at most 20 forms."],
        )

    def test_inlineformset_factory_absolute_max(self):
        author = Author.objects.create(name="Charles Baudelaire")
        BookFormSet = inlineformset_factory(
            Author,
            Book,
            fields="__all__",
            absolute_max=1500,
        )
        data = {
            "book_set-TOTAL_FORMS": "1501",
            "book_set-INITIAL_FORMS": "0",
            "book_set-MAX_NUM_FORMS": "0",
        }
        formset = BookFormSet(data, instance=author)
        self.assertIs(formset.is_valid(), False)
        self.assertEqual(len(formset.forms), 1500)
        self.assertEqual(
            formset.non_form_errors(),
            ["Please submit at most 1000 forms."],
        )

    def test_inlineformset_factory_absolute_max_with_max_num(self):
        author = Author.objects.create(name="Charles Baudelaire")
        BookFormSet = inlineformset_factory(
            Author,
            Book,
            fields="__all__",
            max_num=20,
            absolute_max=100,
        )
        data = {
            "book_set-TOTAL_FORMS": "101",
            "book_set-INITIAL_FORMS": "0",
            "book_set-MAX_NUM_FORMS": "0",
        }
        formset = BookFormSet(data, instance=author)
        self.assertIs(formset.is_valid(), False)
        self.assertEqual(len(formset.forms), 100)
        self.assertEqual(
            formset.non_form_errors(),
            ["Please submit at most 20 forms."],
        )

    def test_modelformset_factory_can_delete_extra(self):
        AuthorFormSet = modelformset_factory(
            Author,
            fields="__all__",
            can_delete=True,
            can_delete_extra=True,
            extra=2,
        )
        formset = AuthorFormSet()
        self.assertEqual(len(formset), 2)
        self.assertIn("DELETE", formset.forms[0].fields)
        self.assertIn("DELETE", formset.forms[1].fields)

    def test_modelformset_factory_disable_delete_extra(self):
        AuthorFormSet = modelformset_factory(
            Author,
            fields="__all__",
            can_delete=True,
            can_delete_extra=False,
            extra=2,
        )
        formset = AuthorFormSet()
        self.assertEqual(len(formset), 2)
        self.assertNotIn("DELETE", formset.forms[0].fields)
        self.assertNotIn("DELETE", formset.forms[1].fields)

    def test_inlineformset_factory_can_delete_extra(self):
        BookFormSet = inlineformset_factory(
            Author,
            Book,
            fields="__all__",
            can_delete=True,
            can_delete_extra=True,
            extra=2,
        )
        formset = BookFormSet()
        self.assertEqual(len(formset), 2)
        self.assertIn("DELETE", formset.forms[0].fields)
        self.assertIn("DELETE", formset.forms[1].fields)

    def test_inlineformset_factory_can_not_delete_extra(self):
        BookFormSet = inlineformset_factory(
            Author,
            Book,
            fields="__all__",
            can_delete=True,
            can_delete_extra=False,
            extra=2,
        )
        formset = BookFormSet()
        self.assertEqual(len(formset), 2)
        self.assertNotIn("DELETE", formset.forms[0].fields)
        self.assertNotIn("DELETE", formset.forms[1].fields)

    def test_inlineformset_factory_passes_renderer(self):
        from django.forms.renderers import Jinja2

        renderer = Jinja2()
        BookFormSet = inlineformset_factory(
            Author,
            Book,
            fields="__all__",
            renderer=renderer,
        )
        formset = BookFormSet()
        self.assertEqual(formset.renderer, renderer)

    def test_modelformset_factory_passes_renderer(self):
        from django.forms.renderers import Jinja2

        renderer = Jinja2()
        BookFormSet = modelformset_factory(Author, fields="__all__", renderer=renderer)
        formset = BookFormSet()
        self.assertEqual(formset.renderer, renderer)

    def test_modelformset_factory_default_renderer(self):
        class CustomRenderer(DjangoTemplates):
            pass

        class ModelFormWithDefaultRenderer(ModelForm):
            default_renderer = CustomRenderer()

        BookFormSet = modelformset_factory(
            Author, form=ModelFormWithDefaultRenderer, fields="__all__"
        )
        formset = BookFormSet()
        self.assertEqual(
            formset.forms[0].renderer, ModelFormWithDefaultRenderer.default_renderer
        )
        self.assertEqual(
            formset.empty_form.renderer, ModelFormWithDefaultRenderer.default_renderer
        )
        self.assertIsInstance(formset.renderer, DjangoTemplates)

    def test_inlineformset_factory_default_renderer(self):
        class CustomRenderer(DjangoTemplates):
            pass

        class ModelFormWithDefaultRenderer(ModelForm):
            default_renderer = CustomRenderer()

        BookFormSet = inlineformset_factory(
            Author,
            Book,
            form=ModelFormWithDefaultRenderer,
            fields="__all__",
        )
        formset = BookFormSet()
        self.assertEqual(
            formset.forms[0].renderer, ModelFormWithDefaultRenderer.default_renderer
        )
        self.assertEqual(
            formset.empty_form.renderer, ModelFormWithDefaultRenderer.default_renderer
        )
        self.assertIsInstance(formset.renderer, DjangoTemplates)