helper functions for creating form classes from django models and database field objects construct and return a model instance from the bound form s cleaneddata but do not save the returned instance to the database leave defaults for fields that aren t in post data except for checkbox inputs because they don t appear in post data if not checked defer saving filetype fields until after the other fields so a callable uploadto can use the values from other fields modelforms return a dict containing the data in instance suitable for passing as a form s initial keyword argument fields is an optional list of field names if provided return only the named exclude is an optional list of field names if provided exclude the named from the returned dict even if they are listed in the fields argument apply limitchoicesto to the formfield s queryset if needed from django db models import exists outerref q if hasattrformfield queryset and hasattrformfield getlimitchoicesto limitchoicesto formfield getlimitchoicesto if limitchoicesto complexfilter limitchoicesto if not isinstancecomplexfilter q complexfilter qlimitchoicesto complexfilter qpkouterrefpk use exists to avoid potential duplicates formfield queryset formfield queryset filter existsformfield queryset model basemanager filtercomplexfilter def fieldsformodel model fieldsnone excludenone widgetsnone formfieldcallbacknone localizedfieldsnone labelsnone helptextsnone errormessagesnone fieldclassesnone applylimitchoicestotrue formdeclaredfieldsnone formdeclaredfields formdeclaredfields or fielddict ignored opts model meta avoid circular import from django db models import field as modelfield sortableprivatefields f for f in opts privatefields if isinstancef modelfield for f in sorted chainopts concretefields sortableprivatefields opts manytomany if not getattrf editable false if fields is not none and f name in fields and exclude is none or f name not in exclude raise fielderror s cannot be specified for s model form as it is a noneditable field f name model name continue if fields is not none and f name not in fields continue if exclude and f name in exclude continue if f name in formdeclaredfields fielddictf name formdeclaredfieldsf name continue kwargs if widgets and f name in widgets kwargswidget widgetsf name if localizedfields allfields or localizedfields and f name in localizedfields kwargslocalize true if labels and f name in labels kwargslabel labelsf name if helptexts and f name in helptexts kwargshelptext helptextsf name if errormessages and f name in errormessages kwargserrormessages errormessagesf name if fieldclasses and f name in fieldclasses kwargsformclass fieldclassesf name if formfieldcallback is none formfield f formfieldkwargs elif not callableformfieldcallback raise typeerrorformfieldcallback must be a function or callable else formfield formfieldcallbackf kwargs if formfield if applylimitchoicesto applylimitchoicestotoformfieldformfield fielddictf name formfield else ignored appendf name if fields fielddict f fielddict getf for f in fields if not exclude or f not in exclude and f not in ignored return fielddict class modelformoptions def initself optionsnone self model getattroptions model none self fields getattroptions fields none self exclude getattroptions exclude none self widgets getattroptions widgets none self localizedfields getattroptions localizedfields none self labels getattroptions labels none self helptexts getattroptions helptexts none self errormessages getattroptions errormessages none self fieldclasses getattroptions fieldclasses none self formfieldcallback getattroptions formfieldcallback none class modelformmetaclassdeclarativefieldsmetaclass def newmcs name bases attrs newclass super newmcs name bases attrs if bases basemodelform return newclass opts newclass meta modelformoptionsgetattrnewclass meta none we check if a string was passed to fields or exclude which is likely to be a mistake where the user typed foo instead of foo for opt in fields exclude localizedfields value getattropts opt if isinstancevalue str and value allfields msg models meta opts cannot be a string did you mean to type values model newclass name opt opt value value raise typeerrormsg if opts model if a model is defined extract form fields from it if opts fields is none and opts exclude is none raise improperlyconfigured creating a modelform without either the fields attribute or the exclude attribute is prohibited form s needs updating name if opts fields allfields sentinel for fieldsformodel to indicate get the list of fields from the model opts fields none fields fieldsformodel opts model opts fields opts exclude opts widgets opts formfieldcallback opts localizedfields opts labels opts helptexts opts errormessages opts fieldclasses limitchoicesto will be applied during modelform init applylimitchoicestofalse formdeclaredfieldsnewclass declaredfields make sure opts fields doesn t specify an invalid field nonemodelfields k for k v in fields items if not v missingfields nonemodelfields differencenewclass declaredfields if missingfields message unknown fields s specified for s message joinmissingfields opts model name raise fielderrormessage include all the other declared fields fields updatenewclass declaredfields else fields newclass declaredfields newclass basefields fields return newclass class basemodelformbaseform altersdata def init self datanone filesnone autoidids prefixnone initialnone errorclasserrorlist labelsuffixnone emptypermittedfalse instancenone userequiredattributenone renderernone opts self meta if opts model is none raise valueerrormodelform has no model class specified if instance is none if we didn t get an instance instantiate a new one self instance opts model objectdata else self instance instance objectdata modeltodictinstance opts fields opts exclude if initial was provided it should override the values from instance if initial is not none objectdata updateinitial self validateunique will be set to true by basemodelform clean it is false by default so overriding self clean and failing to call super will stop validateunique from being called self validateunique false super init data files autoid prefix objectdata errorclass labelsuffix emptypermitted userequiredattributeuserequiredattribute rendererrenderer for formfield in self fields values applylimitchoicestotoformfieldformfield def getvalidationexclusionsself exclude set build up a list of fields that should be excluded from model field validation and unique checks for f in self instance meta fields field f name exclude fields that aren t on the form the developer may be adding these values to the model after form validation if field not in self fields exclude addf name don t perform model validation on fields that were defined manually on the form and excluded via the modelform s meta class see 12901 elif self meta fields and field not in self meta fields exclude addf name elif self meta exclude and field in self meta exclude exclude addf name exclude fields that failed form validation there s no need for the model fields to validate them as well elif field in self errors exclude addf name exclude empty fields that are not required by the form if the underlying model field is required this keeps the model field from raising a required error note don t exclude the field from validation if the model field allows blanks if it does the blank value may be included in a unique check so cannot be excluded from validation else formfield self fieldsfield fieldvalue self cleaneddata getfield if not f blank and not formfield required and fieldvalue in formfield emptyvalues exclude addf name return exclude def cleanself self validateunique true return self cleaneddata def updateerrorsself errors override any validation error messages defined at the model level with those defined at the form level opts self meta allow the model generated by constructinstance to raise validationerror and have them handled in the same way as others if hasattrerrors errordict errordict errors errordict else errordict nonfielderrors errors for field messages in errordict items if field nonfielderrors and opts errormessages and nonfielderrors in opts errormessages errormessages opts errormessagesnonfielderrors elif field in self fields errormessages self fieldsfield errormessages else continue for message in messages if isinstancemessage validationerror and message code in errormessages message message errormessagesmessage code self adderrornone errors def postcleanself opts self meta exclude self getvalidationexclusions foreign keys being used to represent inline relationships are excluded from basic field value validation this is for two reasons firstly the value may not be supplied 12507 the case of providing new values to the admin secondly the object being referred to may not yet fully exist 12749 however these fields must be included in uniqueness checks so this can t be part of getvalidationexclusions for name field in self fields items if isinstancefield inlineforeignkeyfield exclude addname try self instance constructinstance self self instance opts fields opts exclude except validationerror as e self updateerrorse try self instance fullcleanexcludeexclude validateuniquefalse except validationerror as e self updateerrorse validate uniqueness if needed if self validateunique self validateunique def validateuniqueself exclude self getvalidationexclusions try self instance validateuniqueexcludeexclude except validationerror as e self updateerrorse def savem2mself cleaneddata self cleaneddata exclude self meta exclude fields self meta fields opts self instance meta note that for historical reasons we want to include also privatefields here genericrelation was previously a fake m2m field for f in chainopts manytomany opts privatefields if not hasattrf saveformdata continue if fields and f name not in fields continue if exclude and f name in exclude continue if f name in cleaneddata f saveformdataself instance cleaneddataf name def saveself committrue if self errors raise valueerror the s could not be s because the data didn t validate self instance meta objectname created if self instance state adding else changed if commit if committing save the instance and the m2m data immediately self instance save self savem2m else if not committing add a method to the form to allow deferred saving of m2m data self savem2m self savem2m return self instance save altersdata true class modelformbasemodelform metaclassmodelformmetaclass pass def modelformfactory model formmodelform fieldsnone excludenone formfieldcallbacknone widgetsnone localizedfieldsnone labelsnone helptextsnone errormessagesnone fieldclassesnone create the inner meta class fixme ideally we should be able to construct a modelform without creating and passing in a temporary inner class build up a list of attributes that the meta object will have attrs model model if fields is not none attrsfields fields if exclude is not none attrsexclude exclude if widgets is not none attrswidgets widgets if localizedfields is not none attrslocalizedfields localizedfields if labels is not none attrslabels labels if helptexts is not none attrshelptexts helptexts if errormessages is not none attrserrormessages errormessages if fieldclasses is not none attrsfieldclasses fieldclasses if parent form class already has an inner meta the meta we re creating needs to inherit from the parent s inner meta bases form meta if hasattrform meta else meta typemeta bases attrs if formfieldcallback meta formfieldcallback staticmethodformfieldcallback give this new form class a reasonable name classname model name form class attributes for the new form class formclassattrs meta meta if getattrmeta fields none is none and getattrmeta exclude none is none raise improperlyconfigured calling modelformfactory without defining fields or exclude explicitly is prohibited instantiate typeform in order to use the same metaclass as form return typeformclassname form formclassattrs modelformsets class basemodelformsetbaseformset altersdata model none editonly false set of fields that must be unique among forms of this set uniquefields set def init self datanone filesnone autoidids prefixnone querysetnone initialnone kwargs self queryset queryset self initialextra initial super init data data files files autoid autoid prefix prefix kwargs def initialformcountself if the field is a related field fetch the concrete field s that is the ultimate pointedto field s topython the primary key is missing the user may have tampered with post data the primary key exists but is an invalid value the user may have tampered with post data set initial values for extra forms if the queryset isn t already ordered we need to add an artificial ordering here to make sure that all formsets constructed from this queryset have the same form order removed queryset limiting here as per discussion re 13023 on djangodev maxnum should not prevent existing related objectsinlines from being displayed save and return a new model instance for the given form return form savecommitcommit def saveexistingself form obj committrue deletes an existing model instance if commit obj delete def saveself committrue if not commit self savedforms def savem2m for form in self savedforms form savem2m self savem2m savem2m if self editonly return self saveexistingobjectscommit else return self saveexistingobjectscommit self savenewobjectscommit save altersdata true def cleanself self validateunique def validateuniqueself collect uniquechecks and datechecks to run from all the forms alluniquechecks set alldatechecks set formstodelete self deletedforms validforms form for form in self forms if form isvalid and form not in formstodelete for form in validforms exclude form getvalidationexclusions uniquechecks datechecks form instance getuniquechecks excludeexclude includemetaconstraintstrue alluniquechecks updateuniquechecks alldatechecks updatedatechecks errors do each of the unique checks unique and uniquetogether for uclass uniquecheck in alluniquechecks seendata set for form in validforms get the data for the set of fields that must be unique among the forms rowdata field if field in self uniquefields else form cleaneddatafield for field in uniquecheck if field in form cleaneddata reduce model instances to their primary key values rowdata tuple d getpkval if hasattrd getpkval prevent unhashable type list errors later on else tupled if isinstanced list else d for d in rowdata if rowdata and none not in rowdata if we ve already seen it then we have a uniqueness failure if rowdata in seendata poke error messages into the right places and mark the form as invalid errors appendself getuniqueerrormessageuniquecheck form errorsnonfielderrors self errorclass self getformerror rendererself renderer remove the data from the cleaneddata dict since it was invalid for field in uniquecheck if field in form cleaneddata del form cleaneddatafield mark the data as seen seendata addrowdata iterate over each of the date checks now for datecheck in alldatechecks seendata set uclass lookup field uniquefor datecheck for form in validforms see if we have data for both fields if form cleaneddata and form cleaneddatafield is not none and form cleaneddatauniquefor is not none if it s a date lookup we need to get the data for all the fields if lookup date date form cleaneddatauniquefor datedata date year date month date day otherwise it s just the attribute on the datedatetime object else datedata getattrform cleaneddatauniquefor lookup data form cleaneddatafield datedata if we ve already seen it then we have a uniqueness failure if data in seendata poke error messages into the right places and mark the form as invalid errors appendself getdateerrormessagedatecheck form errorsnonfielderrors self errorclass self getformerror rendererself renderer remove the data from the cleaneddata dict since it was invalid del form cleaneddatafield mark the data as seen seendata adddata if errors raise validationerrorerrors def getuniqueerrormessageself uniquecheck if lenuniquecheck 1 return gettextplease correct the duplicate data for fields field uniquecheck0 else return gettext please correct the duplicate data for fields which must be unique field gettextlistuniquecheck and def getdateerrormessageself datecheck return gettext please correct the duplicate data for fieldnames which must be unique for the lookups in datefields fieldname datecheck2 datefield datecheck3 lookup strdatecheck1 def getformerrorself return gettextplease correct the duplicate values below def saveexistingobjectsself committrue self changedobjects self deletedobjects if not self initialforms return savedinstances formstodelete self deletedforms for form in self initialforms obj form instance if the pk is none it means either 1 the object is an unexpected empty model invalid post data such as an object outside the formset s queryset 2 the object was already deleted from the database if obj pk is none continue if form in formstodelete self deletedobjects appendobj self deleteexistingobj commitcommit elif form haschanged self changedobjects appendobj form changeddata savedinstances appendself saveexistingform obj commitcommit if not commit self savedforms appendform return savedinstances def savenewobjectsself committrue self newobjects for form in self extraforms if not form haschanged continue if someone has marked an add form for deletion don t save the object if self candelete and self shoulddeleteformform continue self newobjects appendself savenewform commitcommit if not commit self savedforms appendform return self newobjects def addfieldsself form index if a pk isn t editable then it won t be on the form so we need to add it here so we can tell which object is which when we get the data back generally pk editable should be false but for some reason autocreated pk fields and autofield s editable attribute is true so check for that as well if we re adding the related instance ignore its primary key as it could be an autogenerated default which isn t actually in the database return a formset class for the given django model class meta getattrform meta none if getattrmeta fields fields is none and getattrmeta exclude exclude is none raise improperlyconfigured calling modelformsetfactory without defining fields or exclude explicitly is prohibited form modelformfactory model formform fieldsfields excludeexclude formfieldcallbackformfieldcallback widgetswidgets localizedfieldslocalizedfields labelslabels helptextshelptexts errormessageserrormessages fieldclassesfieldclasses formset formsetfactory form formset extraextra minnumminnum maxnummaxnum canordercanorder candeletecandelete validateminvalidatemin validatemaxvalidatemax absolutemaxabsolutemax candeleteextracandeleteextra rendererrenderer formset model model formset editonly editonly return formset inlineformsets class baseinlineformsetbasemodelformset add the generated field to form meta fields if it s defined to make sure validation isn t skipped on that field allow modifying an immutable querydict remove the primary key from the form s data we are only creating new instances remove the foreign key from the form s data set the fk value here so that the form can do its validation ensure the latest copy of the related instance is present on each form it may have been saved after the formset was originally instantiated the foreign key field might not be on the form so we poke at the model field to get the label since we need that for error messages the inlineforeignkeyfield assumes that the foreign key relation is based on the parent model s pk if this isn t the case set tofield to correctly resolve the initial form value if we re adding a new object ignore a parent s autogenerated key as it will be regenerated on the save request don t ignore a parent s autogenerated key if it s not the parent model s pk and form data is provided find and return the foreignkey from model to parent if there is one return none if canfail is true and no such field exists if fkname is provided assume it is the name of the foreignkey field unless canfail is true raise an exception if there isn t a foreignkey from model to parentmodel avoid circular import foreignkey to proxy models foreignkey to concrete models try to discover what the foreignkey from model to parentmodel is return an inlineformset for the given kwargs fkname must be provided if model has more than one foreignkey to parentmodel enforce a maxnum1 when the foreign key to the parent model is unique fields a basic integer field that deals with validating the given value to a given parent instance in an inline if there is no value act as we did before ensure the we compare the values as equal types can t use iterator when queryset uses prefetchrelated count adds a query but uses less memory since the queryset results won t be cached in most cases the choices will only be iterated on and len won t be called a choicefield whose choices are a model queryset this class is a subclass of choicefield for purity but it doesn t actually use any of choicefield s implementation defaulterrormessages invalidchoice select a valid choice that choice is not one of the available choices iterator modelchoiceiterator def init self queryset emptylabel requiredtrue widgetnone labelnone initialnone helptext tofieldnamenone limitchoicestonone blankfalse kwargs call field instead of choicefield init because we don t need choicefield init field init self requiredrequired widgetwidget labellabel initialinitial helptexthelptext kwargs if required and initial is not none or isinstanceself widget radioselect and not blank self emptylabel none else self emptylabel emptylabel self queryset queryset self limitchoicesto limitchoicesto limit the queryset later self tofieldname tofieldname def getlimitchoicestoself if callableself limitchoicesto return self limitchoicesto return self limitchoicesto def deepcopyself memo result superchoicefield self deepcopymemo need to force a new modelchoiceiterator to be created bug 11183 if self queryset is not none result queryset self queryset all return result def getquerysetself return self queryset def setquerysetself queryset self queryset none if queryset is none else queryset all self widget choices self choices queryset propertygetqueryset setqueryset this method will be used to create object labels by the querysetiterator override it to customize the label def labelfrominstanceself obj return strobj def getchoicesself if self choices is set then somebody must have manually set the property self choices in this case just return self choices if hasattrself choices return self choices otherwise execute the queryset in self queryset to determine the choices dynamically return a fresh modelchoiceiterator that has not been consumed note that we re instantiating a new modelchoiceiterator each time getchoices is called and thus each time self choices is accessed so that we can ensure the queryset has not been consumed this construct might look complicated but it allows for lazy evaluation of the queryset return self iteratorself choices propertygetchoices choicefield choices fset def preparevalueself value if hasattrvalue meta if self tofieldname return value serializablevalueself tofieldname else return value pk return super preparevaluevalue def topythonself value if value in self emptyvalues return none try key self tofieldname or pk if isinstancevalue self queryset model value getattrvalue key value self queryset getkey value except valueerror typeerror self queryset model doesnotexist raise validationerror self errormessagesinvalidchoice codeinvalidchoice paramsvalue value return value def validateself value return field validateself value def haschangedself initial data if self disabled return false initialvalue initial if initial is not none else datavalue data if data is not none else return strself preparevalueinitialvalue strdatavalue class modelmultiplechoicefieldmodelchoicefield since this overrides the inherited modelchoicefield clean we run custom validators here given a list of possible pk values return a queryset of the corresponding objects raise a validationerror if a given value is invalid not a valid pk not in the queryset etc deduplicate given values to avoid creating many querysets or requiring the database backend deduplicate efficiently list of lists isn t hashable for example construct and return a model instance from the bound form s cleaned_data but do not save the returned instance to the database leave defaults for fields that aren t in post data except for checkbox inputs because they don t appear in post data if not checked defer saving file type fields until after the other fields so a callable upload_to can use the values from other fields modelforms return a dict containing the data in instance suitable for passing as a form s initial keyword argument fields is an optional list of field names if provided return only the named exclude is an optional list of field names if provided exclude the named from the returned dict even if they are listed in the fields argument apply limit_choices_to to the formfield s queryset if needed use exists to avoid potential duplicates return a dictionary containing form fields for the given model fields is an optional list of field names if provided return only the named fields exclude is an optional list of field names if provided exclude the named fields from the returned fields even if they are listed in the fields argument widgets is a dictionary of model field names mapped to a widget formfield_callback is a callable that takes a model field and returns a form field localized_fields is a list of names of fields which should be localized labels is a dictionary of model field names mapped to a label help_texts is a dictionary of model field names mapped to a help text error_messages is a dictionary of model field names mapped to a dictionary of error messages field_classes is a dictionary of model field names mapped to a form field class apply_limit_choices_to is a boolean indicating if limit_choices_to should be applied to a field s queryset form_declared_fields is a dictionary of form fields created directly on a form avoid circular import we check if a string was passed to fields or exclude which is likely to be a mistake where the user typed foo instead of foo if a model is defined extract form fields from it sentinel for fields_for_model to indicate get the list of fields from the model limit_choices_to will be applied during modelform __init__ make sure opts fields doesn t specify an invalid field include all the other declared fields if we didn t get an instance instantiate a new one if initial was provided it should override the values from instance self _validate_unique will be set to true by basemodelform clean it is false by default so overriding self clean and failing to call super will stop validate_unique from being called for backwards compatibility exclude several types of fields from model validation see tickets 12507 12521 12553 build up a list of fields that should be excluded from model field validation and unique checks exclude fields that aren t on the form the developer may be adding these values to the model after form validation don t perform model validation on fields that were defined manually on the form and excluded via the modelform s meta class see 12901 exclude fields that failed form validation there s no need for the model fields to validate them as well exclude empty fields that are not required by the form if the underlying model field is required this keeps the model field from raising a required error note don t exclude the field from validation if the model field allows blanks if it does the blank value may be included in a unique check so cannot be excluded from validation override any validation error messages defined at the model level with those defined at the form level allow the model generated by construct_instance to raise validationerror and have them handled in the same way as others foreign keys being used to represent inline relationships are excluded from basic field value validation this is for two reasons firstly the value may not be supplied 12507 the case of providing new values to the admin secondly the object being referred to may not yet fully exist 12749 however these fields must be included in uniqueness checks so this can t be part of _get_validation_exclusions validate uniqueness if needed call the instance s validate_unique method and update the form s validation errors if any were raised save the many to many fields and generic relations for this form note that for historical reasons we want to include also private_fields here genericrelation was previously a fake m2m field save this form s self instance object if commit true otherwise add a save_m2m method to the form which can be called after the instance is saved manually at a later time return the model instance if committing save the instance and the m2m data immediately if not committing add a method to the form to allow deferred saving of m2m data return a modelform containing form fields for the given model you can optionally pass a form argument to use as a starting point for constructing the modelform fields is an optional list of field names if provided include only the named fields in the returned fields if omitted or __all__ use all fields exclude is an optional list of field names if provided exclude the named fields from the returned fields even if they are listed in the fields argument widgets is a dictionary of model field names mapped to a widget localized_fields is a list of names of fields which should be localized formfield_callback is a callable that takes a model field and returns a form field labels is a dictionary of model field names mapped to a label help_texts is a dictionary of model field names mapped to a help text error_messages is a dictionary of model field names mapped to a dictionary of error messages field_classes is a dictionary of model field names mapped to a form field class create the inner meta class fixme ideally we should be able to construct a modelform without creating and passing in a temporary inner class build up a list of attributes that the meta object will have if parent form class already has an inner meta the meta we re creating needs to inherit from the parent s inner meta give this new form class a reasonable name class attributes for the new form class instantiate type form in order to use the same metaclass as form modelformsets a formset for editing a queryset and or adding new objects to it set of fields that must be unique among forms of this set return the number of forms that are required in this formset if the field is a related field fetch the concrete field s that is the ultimate pointed to field s to_python the primary key is missing the user may have tampered with post data the primary key exists but is an invalid value the user may have tampered with post data set initial values for extra forms if the queryset isn t already ordered we need to add an artificial ordering here to make sure that all formsets constructed from this queryset have the same form order removed queryset limiting here as per discussion re 13023 on django dev max_num should not prevent existing related objects inlines from being displayed save and return a new model instance for the given form save and return an existing model instance for the given form deletes an existing model instance save model instances for every form adding and changing instances as necessary and return the list of instances collect unique_checks and date_checks to run from all the forms do each of the unique checks unique and unique_together get the data for the set of fields that must be unique among the forms reduce model instances to their primary key values prevent unhashable type list errors later on if we ve already seen it then we have a uniqueness failure poke error messages into the right places and mark the form as invalid remove the data from the cleaned_data dict since it was invalid mark the data as seen iterate over each of the date checks now see if we have data for both fields if it s a date lookup we need to get the data for all the fields otherwise it s just the attribute on the date datetime object if we ve already seen it then we have a uniqueness failure poke error messages into the right places and mark the form as invalid remove the data from the cleaned_data dict since it was invalid mark the data as seen if the pk is none it means either 1 the object is an unexpected empty model invalid post data such as an object outside the formset s queryset 2 the object was already deleted from the database if someone has marked an add form for deletion don t save the object add a hidden field for the object s primary key if a pk isn t editable then it won t be on the form so we need to add it here so we can tell which object is which when we get the data back generally pk editable should be false but for some reason auto_created pk fields and autofield s editable attribute is true so check for that as well if we re adding the related instance ignore its primary key as it could be an auto generated default which isn t actually in the database return a formset class for the given django model class inlineformsets a formset for child objects related to a parent add the generated field to form _meta fields if it s defined to make sure validation isn t skipped on that field allow modifying an immutable querydict remove the primary key from the form s data we are only creating new instances remove the foreign key from the form s data set the fk value here so that the form can do its validation ensure the latest copy of the related instance is present on each form it may have been saved after the formset was originally instantiated the foreign key field might not be on the form so we poke at the model field to get the label since we need that for error messages the inlineforeignkeyfield assumes that the foreign key relation is based on the parent model s pk if this isn t the case set to_field to correctly resolve the initial form value if we re adding a new object ignore a parent s auto generated key as it will be regenerated on the save request don t ignore a parent s auto generated key if it s not the parent model s pk and form data is provided find and return the foreignkey from model to parent if there is one return none if can_fail is true and no such field exists if fk_name is provided assume it is the name of the foreignkey field unless can_fail is true raise an exception if there isn t a foreignkey from model to parent_model avoid circular import foreignkey to proxy models foreignkey to concrete models try to discover what the foreignkey from model to parent_model is return an inlineformset for the given kwargs fk_name must be provided if model has more than one foreignkey to parent_model enforce a max_num 1 when the foreign key to the parent model is unique fields a basic integer field that deals with validating the given value to a given parent instance in an inline if there is no value act as we did before ensure the we compare the values as equal types can t use iterator when queryset uses prefetch_related count adds a query but uses less memory since the queryset results won t be cached in most cases the choices will only be iterated on and __len__ won t be called a choicefield whose choices are a model queryset this class is a subclass of choicefield for purity but it doesn t actually use any of choicefield s implementation call field instead of choicefield __init__ because we don t need choicefield __init__ limit the queryset later return limit_choices_to for this form field if it is a callable invoke it and return the result need to force a new modelchoiceiterator to be created bug 11183 this method will be used to create object labels by the querysetiterator override it to customize the label convert objects into strings and generate the labels for the choices presented by this object subclasses can override this method to customize the display of the choices if self _choices is set then somebody must have manually set the property self choices in this case just return self _choices otherwise execute the queryset in self queryset to determine the choices dynamically return a fresh modelchoiceiterator that has not been consumed note that we re instantiating a new modelchoiceiterator each time _get_choices is called and thus each time self choices is accessed so that we can ensure the queryset has not been consumed this construct might look complicated but it allows for lazy evaluation of the queryset a multiplechoicefield whose choices are a model queryset since this overrides the inherited modelchoicefield clean we run custom validators here given a list of possible pk values return a queryset of the corresponding objects raise a validationerror if a given value is invalid not a valid pk not in the queryset etc deduplicate given values to avoid creating many querysets or requiring the database backend deduplicate efficiently list of lists isn t hashable for example
from itertools import chain

from django.core.exceptions import (
    NON_FIELD_ERRORS,
    FieldError,
    ImproperlyConfigured,
    ValidationError,
)
from django.db.models.utils import AltersData
from django.forms.fields import ChoiceField, Field
from django.forms.forms import BaseForm, DeclarativeFieldsMetaclass
from django.forms.formsets import BaseFormSet, formset_factory
from django.forms.utils import ErrorList
from django.forms.widgets import (
    HiddenInput,
    MultipleHiddenInput,
    RadioSelect,
    SelectMultiple,
)
from django.utils.choices import BaseChoiceIterator
from django.utils.text import capfirst, get_text_list
from django.utils.translation import gettext
from django.utils.translation import gettext_lazy as _

__all__ = (
    "ModelForm",
    "BaseModelForm",
    "model_to_dict",
    "fields_for_model",
    "ModelChoiceField",
    "ModelMultipleChoiceField",
    "ALL_FIELDS",
    "BaseModelFormSet",
    "modelformset_factory",
    "BaseInlineFormSet",
    "inlineformset_factory",
    "modelform_factory",
)

ALL_FIELDS = "__all__"


def construct_instance(form, instance, fields=None, exclude=None):
    
    from django.db import models

    opts = instance._meta

    cleaned_data = form.cleaned_data
    file_field_list = []
    for f in opts.fields:
        if (
            not f.editable
            or isinstance(f, models.AutoField)
            or f.name not in cleaned_data
        ):
            continue
        if fields is not None and f.name not in fields:
            continue
        if exclude and f.name in exclude:
            continue
        
        
        if (
            f.has_default()
            and form[f.name].field.widget.value_omitted_from_data(
                form.data, form.files, form.add_prefix(f.name)
            )
            and cleaned_data.get(f.name) in form[f.name].field.empty_values
        ):
            continue
        
        
        if isinstance(f, models.FileField):
            file_field_list.append(f)
        else:
            f.save_form_data(instance, cleaned_data[f.name])

    for f in file_field_list:
        f.save_form_data(instance, cleaned_data[f.name])

    return instance





def model_to_dict(instance, fields=None, exclude=None):
    
    opts = instance._meta
    data = {}
    for f in chain(opts.concrete_fields, opts.private_fields, opts.many_to_many):
        if not getattr(f, "editable", False):
            continue
        if fields is not None and f.name not in fields:
            continue
        if exclude and f.name in exclude:
            continue
        data[f.name] = f.value_from_object(instance)
    return data


def apply_limit_choices_to_to_formfield(formfield):
    
    from django.db.models import Exists, OuterRef, Q

    if hasattr(formfield, "queryset") and hasattr(formfield, "get_limit_choices_to"):
        limit_choices_to = formfield.get_limit_choices_to()
        if limit_choices_to:
            complex_filter = limit_choices_to
            if not isinstance(complex_filter, Q):
                complex_filter = Q(**limit_choices_to)
            complex_filter &= Q(pk=OuterRef("pk"))
            
            formfield.queryset = formfield.queryset.filter(
                Exists(formfield.queryset.model._base_manager.filter(complex_filter)),
            )


def fields_for_model(
    model,
    fields=None,
    exclude=None,
    widgets=None,
    formfield_callback=None,
    localized_fields=None,
    labels=None,
    help_texts=None,
    error_messages=None,
    field_classes=None,
    *,
    apply_limit_choices_to=True,
    form_declared_fields=None,
):
    
    form_declared_fields = form_declared_fields or {}
    field_dict = {}
    ignored = []
    opts = model._meta
    
    from django.db.models import Field as ModelField

    sortable_private_fields = [
        f for f in opts.private_fields if isinstance(f, ModelField)
    ]
    for f in sorted(
        chain(opts.concrete_fields, sortable_private_fields, opts.many_to_many)
    ):
        if not getattr(f, "editable", False):
            if (
                fields is not None
                and f.name in fields
                and (exclude is None or f.name not in exclude)
            ):
                raise FieldError(
                    "'%s' cannot be specified for %s model form as it is a "
                    "non-editable field" % (f.name, model.__name__)
                )
            continue
        if fields is not None and f.name not in fields:
            continue
        if exclude and f.name in exclude:
            continue
        if f.name in form_declared_fields:
            field_dict[f.name] = form_declared_fields[f.name]
            continue

        kwargs = {}
        if widgets and f.name in widgets:
            kwargs["widget"] = widgets[f.name]
        if localized_fields == ALL_FIELDS or (
            localized_fields and f.name in localized_fields
        ):
            kwargs["localize"] = True
        if labels and f.name in labels:
            kwargs["label"] = labels[f.name]
        if help_texts and f.name in help_texts:
            kwargs["help_text"] = help_texts[f.name]
        if error_messages and f.name in error_messages:
            kwargs["error_messages"] = error_messages[f.name]
        if field_classes and f.name in field_classes:
            kwargs["form_class"] = field_classes[f.name]

        if formfield_callback is None:
            formfield = f.formfield(**kwargs)
        elif not callable(formfield_callback):
            raise TypeError("formfield_callback must be a function or callable")
        else:
            formfield = formfield_callback(f, **kwargs)

        if formfield:
            if apply_limit_choices_to:
                apply_limit_choices_to_to_formfield(formfield)
            field_dict[f.name] = formfield
        else:
            ignored.append(f.name)
    if fields:
        field_dict = {
            f: field_dict.get(f)
            for f in fields
            if (not exclude or f not in exclude) and f not in ignored
        }
    return field_dict


class ModelFormOptions:
    def __init__(self, options=None):
        self.model = getattr(options, "model", None)
        self.fields = getattr(options, "fields", None)
        self.exclude = getattr(options, "exclude", None)
        self.widgets = getattr(options, "widgets", None)
        self.localized_fields = getattr(options, "localized_fields", None)
        self.labels = getattr(options, "labels", None)
        self.help_texts = getattr(options, "help_texts", None)
        self.error_messages = getattr(options, "error_messages", None)
        self.field_classes = getattr(options, "field_classes", None)
        self.formfield_callback = getattr(options, "formfield_callback", None)


class ModelFormMetaclass(DeclarativeFieldsMetaclass):
    def __new__(mcs, name, bases, attrs):
        new_class = super().__new__(mcs, name, bases, attrs)

        if bases == (BaseModelForm,):
            return new_class

        opts = new_class._meta = ModelFormOptions(getattr(new_class, "Meta", None))

        
        
        
        for opt in ["fields", "exclude", "localized_fields"]:
            value = getattr(opts, opt)
            if isinstance(value, str) and value != ALL_FIELDS:
                msg = (
                    "%(model)s.Meta.%(opt)s cannot be a string. "
                    "Did you mean to type: ('%(value)s',)?"
                    % {
                        "model": new_class.__name__,
                        "opt": opt,
                        "value": value,
                    }
                )
                raise TypeError(msg)

        if opts.model:
            
            if opts.fields is None and opts.exclude is None:
                raise ImproperlyConfigured(
                    "Creating a ModelForm without either the 'fields' attribute "
                    "or the 'exclude' attribute is prohibited; form %s "
                    "needs updating." % name
                )

            if opts.fields == ALL_FIELDS:
                
                
                opts.fields = None

            fields = fields_for_model(
                opts.model,
                opts.fields,
                opts.exclude,
                opts.widgets,
                opts.formfield_callback,
                opts.localized_fields,
                opts.labels,
                opts.help_texts,
                opts.error_messages,
                opts.field_classes,
                
                apply_limit_choices_to=False,
                form_declared_fields=new_class.declared_fields,
            )

            
            none_model_fields = {k for k, v in fields.items() if not v}
            missing_fields = none_model_fields.difference(new_class.declared_fields)
            if missing_fields:
                message = "Unknown field(s) (%s) specified for %s"
                message %= (", ".join(missing_fields), opts.model.__name__)
                raise FieldError(message)
            
            fields.update(new_class.declared_fields)
        else:
            fields = new_class.declared_fields

        new_class.base_fields = fields

        return new_class


class BaseModelForm(BaseForm, AltersData):
    def __init__(
        self,
        data=None,
        files=None,
        auto_id="id_%s",
        prefix=None,
        initial=None,
        error_class=ErrorList,
        label_suffix=None,
        empty_permitted=False,
        instance=None,
        use_required_attribute=None,
        renderer=None,
    ):
        opts = self._meta
        if opts.model is None:
            raise ValueError("ModelForm has no model class specified.")
        if instance is None:
            
            self.instance = opts.model()
            object_data = {}
        else:
            self.instance = instance
            object_data = model_to_dict(instance, opts.fields, opts.exclude)
        
        if initial is not None:
            object_data.update(initial)
        
        
        
        self._validate_unique = False
        super().__init__(
            data,
            files,
            auto_id,
            prefix,
            object_data,
            error_class,
            label_suffix,
            empty_permitted,
            use_required_attribute=use_required_attribute,
            renderer=renderer,
        )
        for formfield in self.fields.values():
            apply_limit_choices_to_to_formfield(formfield)

    def _get_validation_exclusions(self):
        
        exclude = set()
        
        
        for f in self.instance._meta.fields:
            field = f.name
            
            
            if field not in self.fields:
                exclude.add(f.name)

            
            
            
            elif self._meta.fields and field not in self._meta.fields:
                exclude.add(f.name)
            elif self._meta.exclude and field in self._meta.exclude:
                exclude.add(f.name)

            
            
            elif field in self._errors:
                exclude.add(f.name)

            
            
            
            
            
            
            else:
                form_field = self.fields[field]
                field_value = self.cleaned_data.get(field)
                if (
                    not f.blank
                    and not form_field.required
                    and field_value in form_field.empty_values
                ):
                    exclude.add(f.name)
        return exclude

    def clean(self):
        self._validate_unique = True
        return self.cleaned_data

    def _update_errors(self, errors):
        
        
        opts = self._meta

        
        
        if hasattr(errors, "error_dict"):
            error_dict = errors.error_dict
        else:
            error_dict = {NON_FIELD_ERRORS: errors}

        for field, messages in error_dict.items():
            if (
                field == NON_FIELD_ERRORS
                and opts.error_messages
                and NON_FIELD_ERRORS in opts.error_messages
            ):
                error_messages = opts.error_messages[NON_FIELD_ERRORS]
            elif field in self.fields:
                error_messages = self.fields[field].error_messages
            else:
                continue

            for message in messages:
                if (
                    isinstance(message, ValidationError)
                    and message.code in error_messages
                ):
                    message.message = error_messages[message.code]

        self.add_error(None, errors)

    def _post_clean(self):
        opts = self._meta

        exclude = self._get_validation_exclusions()

        
        
        
        
        
        
        
        for name, field in self.fields.items():
            if isinstance(field, InlineForeignKeyField):
                exclude.add(name)

        try:
            self.instance = construct_instance(
                self, self.instance, opts.fields, opts.exclude
            )
        except ValidationError as e:
            self._update_errors(e)

        try:
            self.instance.full_clean(exclude=exclude, validate_unique=False)
        except ValidationError as e:
            self._update_errors(e)

        
        if self._validate_unique:
            self.validate_unique()

    def validate_unique(self):
        
        exclude = self._get_validation_exclusions()
        try:
            self.instance.validate_unique(exclude=exclude)
        except ValidationError as e:
            self._update_errors(e)

    def _save_m2m(self):
        
        cleaned_data = self.cleaned_data
        exclude = self._meta.exclude
        fields = self._meta.fields
        opts = self.instance._meta
        
        
        
        for f in chain(opts.many_to_many, opts.private_fields):
            if not hasattr(f, "save_form_data"):
                continue
            if fields and f.name not in fields:
                continue
            if exclude and f.name in exclude:
                continue
            if f.name in cleaned_data:
                f.save_form_data(self.instance, cleaned_data[f.name])

    def save(self, commit=True):
        
        if self.errors:
            raise ValueError(
                "The %s could not be %s because the data didn't validate."
                % (
                    self.instance._meta.object_name,
                    "created" if self.instance._state.adding else "changed",
                )
            )
        if commit:
            
            self.instance.save()
            self._save_m2m()
        else:
            
            
            self.save_m2m = self._save_m2m
        return self.instance

    save.alters_data = True


class ModelForm(BaseModelForm, metaclass=ModelFormMetaclass):
    pass


def modelform_factory(
    model,
    form=ModelForm,
    fields=None,
    exclude=None,
    formfield_callback=None,
    widgets=None,
    localized_fields=None,
    labels=None,
    help_texts=None,
    error_messages=None,
    field_classes=None,
):
    
    
    
    

    
    attrs = {"model": model}
    if fields is not None:
        attrs["fields"] = fields
    if exclude is not None:
        attrs["exclude"] = exclude
    if widgets is not None:
        attrs["widgets"] = widgets
    if localized_fields is not None:
        attrs["localized_fields"] = localized_fields
    if labels is not None:
        attrs["labels"] = labels
    if help_texts is not None:
        attrs["help_texts"] = help_texts
    if error_messages is not None:
        attrs["error_messages"] = error_messages
    if field_classes is not None:
        attrs["field_classes"] = field_classes

    
    
    bases = (form.Meta,) if hasattr(form, "Meta") else ()
    Meta = type("Meta", bases, attrs)
    if formfield_callback:
        Meta.formfield_callback = staticmethod(formfield_callback)
    
    class_name = model.__name__ + "Form"

    
    form_class_attrs = {"Meta": Meta}

    if getattr(Meta, "fields", None) is None and getattr(Meta, "exclude", None) is None:
        raise ImproperlyConfigured(
            "Calling modelform_factory without defining 'fields' or "
            "'exclude' explicitly is prohibited."
        )

    
    return type(form)(class_name, (form,), form_class_attrs)





class BaseModelFormSet(BaseFormSet, AltersData):
    

    model = None
    edit_only = False

    
    unique_fields = set()

    def __init__(
        self,
        data=None,
        files=None,
        auto_id="id_%s",
        prefix=None,
        queryset=None,
        *,
        initial=None,
        **kwargs,
    ):
        self.queryset = queryset
        self.initial_extra = initial
        super().__init__(
            **{
                "data": data,
                "files": files,
                "auto_id": auto_id,
                "prefix": prefix,
                **kwargs,
            }
        )

    def initial_form_count(self):
        
        if not self.is_bound:
            return len(self.get_queryset())
        return super().initial_form_count()

    def _existing_object(self, pk):
        if not hasattr(self, "_object_dict"):
            self._object_dict = {o.pk: o for o in self.get_queryset()}
        return self._object_dict.get(pk)

    def _get_to_python(self, field):
        
        while field.remote_field is not None:
            field = field.remote_field.get_related_field()
        return field.to_python

    def _construct_form(self, i, **kwargs):
        pk_required = i < self.initial_form_count()
        if pk_required:
            if self.is_bound:
                pk_key = "%s-%s" % (self.add_prefix(i), self.model._meta.pk.name)
                try:
                    pk = self.data[pk_key]
                except KeyError:
                    
                    
                    pass
                else:
                    to_python = self._get_to_python(self.model._meta.pk)
                    try:
                        pk = to_python(pk)
                    except ValidationError:
                        
                        
                        pass
                    else:
                        kwargs["instance"] = self._existing_object(pk)
            else:
                kwargs["instance"] = self.get_queryset()[i]
        elif self.initial_extra:
            
            try:
                kwargs["initial"] = self.initial_extra[i - self.initial_form_count()]
            except IndexError:
                pass
        form = super()._construct_form(i, **kwargs)
        if pk_required:
            form.fields[self.model._meta.pk.name].required = True
        return form

    def get_queryset(self):
        if not hasattr(self, "_queryset"):
            if self.queryset is not None:
                qs = self.queryset
            else:
                qs = self.model._default_manager.get_queryset()

            
            
            
            if not qs.ordered:
                qs = qs.order_by(self.model._meta.pk.name)

            
            
            
            self._queryset = qs
        return self._queryset

    def save_new(self, form, commit=True):
        
        return form.save(commit=commit)

    def save_existing(self, form, obj, commit=True):
        
        return form.save(commit=commit)

    def delete_existing(self, obj, commit=True):
        
        if commit:
            obj.delete()

    def save(self, commit=True):
        
        if not commit:
            self.saved_forms = []

            def save_m2m():
                for form in self.saved_forms:
                    form.save_m2m()

            self.save_m2m = save_m2m
        if self.edit_only:
            return self.save_existing_objects(commit)
        else:
            return self.save_existing_objects(commit) + self.save_new_objects(commit)

    save.alters_data = True

    def clean(self):
        self.validate_unique()

    def validate_unique(self):
        
        all_unique_checks = set()
        all_date_checks = set()
        forms_to_delete = self.deleted_forms
        valid_forms = [
            form
            for form in self.forms
            if form.is_valid() and form not in forms_to_delete
        ]
        for form in valid_forms:
            exclude = form._get_validation_exclusions()
            unique_checks, date_checks = form.instance._get_unique_checks(
                exclude=exclude,
                include_meta_constraints=True,
            )
            all_unique_checks.update(unique_checks)
            all_date_checks.update(date_checks)

        errors = []
        
        for uclass, unique_check in all_unique_checks:
            seen_data = set()
            for form in valid_forms:
                
                
                row_data = (
                    field if field in self.unique_fields else form.cleaned_data[field]
                    for field in unique_check
                    if field in form.cleaned_data
                )
                
                row_data = tuple(
                    d._get_pk_val() if hasattr(d, "_get_pk_val")
                    
                    else tuple(d) if isinstance(d, list) else d
                    for d in row_data
                )
                if row_data and None not in row_data:
                    
                    if row_data in seen_data:
                        
                        
                        errors.append(self.get_unique_error_message(unique_check))
                        form._errors[NON_FIELD_ERRORS] = self.error_class(
                            [self.get_form_error()],
                            renderer=self.renderer,
                        )
                        
                        
                        for field in unique_check:
                            if field in form.cleaned_data:
                                del form.cleaned_data[field]
                    
                    seen_data.add(row_data)
        
        for date_check in all_date_checks:
            seen_data = set()
            uclass, lookup, field, unique_for = date_check
            for form in valid_forms:
                
                if (
                    form.cleaned_data
                    and form.cleaned_data[field] is not None
                    and form.cleaned_data[unique_for] is not None
                ):
                    
                    if lookup == "date":
                        date = form.cleaned_data[unique_for]
                        date_data = (date.year, date.month, date.day)
                    
                    
                    else:
                        date_data = (getattr(form.cleaned_data[unique_for], lookup),)
                    data = (form.cleaned_data[field],) + date_data
                    
                    if data in seen_data:
                        
                        
                        errors.append(self.get_date_error_message(date_check))
                        form._errors[NON_FIELD_ERRORS] = self.error_class(
                            [self.get_form_error()],
                            renderer=self.renderer,
                        )
                        
                        
                        del form.cleaned_data[field]
                    
                    seen_data.add(data)

        if errors:
            raise ValidationError(errors)

    def get_unique_error_message(self, unique_check):
        if len(unique_check) == 1:
            return gettext("Please correct the duplicate data for %(field)s.") % {
                "field": unique_check[0],
            }
        else:
            return gettext(
                "Please correct the duplicate data for %(field)s, which must be unique."
            ) % {
                "field": get_text_list(unique_check, _("and")),
            }

    def get_date_error_message(self, date_check):
        return gettext(
            "Please correct the duplicate data for %(field_name)s "
            "which must be unique for the %(lookup)s in %(date_field)s."
        ) % {
            "field_name": date_check[2],
            "date_field": date_check[3],
            "lookup": str(date_check[1]),
        }

    def get_form_error(self):
        return gettext("Please correct the duplicate values below.")

    def save_existing_objects(self, commit=True):
        self.changed_objects = []
        self.deleted_objects = []
        if not self.initial_forms:
            return []

        saved_instances = []
        forms_to_delete = self.deleted_forms
        for form in self.initial_forms:
            obj = form.instance
            
            
            
            
            if obj.pk is None:
                continue
            if form in forms_to_delete:
                self.deleted_objects.append(obj)
                self.delete_existing(obj, commit=commit)
            elif form.has_changed():
                self.changed_objects.append((obj, form.changed_data))
                saved_instances.append(self.save_existing(form, obj, commit=commit))
                if not commit:
                    self.saved_forms.append(form)
        return saved_instances

    def save_new_objects(self, commit=True):
        self.new_objects = []
        for form in self.extra_forms:
            if not form.has_changed():
                continue
            
            
            if self.can_delete and self._should_delete_form(form):
                continue
            self.new_objects.append(self.save_new(form, commit=commit))
            if not commit:
                self.saved_forms.append(form)
        return self.new_objects

    def add_fields(self, form, index):
        
        from django.db.models import AutoField, ForeignKey, OneToOneField

        self._pk_field = pk = self.model._meta.pk
        
        
        
        
        

        def pk_is_not_editable(pk):
            return (
                (not pk.editable)
                or (pk.auto_created or isinstance(pk, AutoField))
                or (
                    pk.remote_field
                    and pk.remote_field.parent_link
                    and pk_is_not_editable(pk.remote_field.model._meta.pk)
                )
            )

        if pk_is_not_editable(pk) or pk.name not in form.fields:
            if form.is_bound:
                
                
                
                pk_value = None if form.instance._state.adding else form.instance.pk
            else:
                try:
                    if index is not None:
                        pk_value = self.get_queryset()[index].pk
                    else:
                        pk_value = None
                except IndexError:
                    pk_value = None
            if isinstance(pk, (ForeignKey, OneToOneField)):
                qs = pk.remote_field.model._default_manager.get_queryset()
            else:
                qs = self.model._default_manager.get_queryset()
            qs = qs.using(form.instance._state.db)
            if form._meta.widgets:
                widget = form._meta.widgets.get(self._pk_field.name, HiddenInput)
            else:
                widget = HiddenInput
            form.fields[self._pk_field.name] = ModelChoiceField(
                qs, initial=pk_value, required=False, widget=widget
            )
        super().add_fields(form, index)


def modelformset_factory(
    model,
    form=ModelForm,
    formfield_callback=None,
    formset=BaseModelFormSet,
    extra=1,
    can_delete=False,
    can_order=False,
    max_num=None,
    fields=None,
    exclude=None,
    widgets=None,
    validate_max=False,
    localized_fields=None,
    labels=None,
    help_texts=None,
    error_messages=None,
    min_num=None,
    validate_min=False,
    field_classes=None,
    absolute_max=None,
    can_delete_extra=True,
    renderer=None,
    edit_only=False,
):
    
    meta = getattr(form, "Meta", None)
    if (
        getattr(meta, "fields", fields) is None
        and getattr(meta, "exclude", exclude) is None
    ):
        raise ImproperlyConfigured(
            "Calling modelformset_factory without defining 'fields' or "
            "'exclude' explicitly is prohibited."
        )

    form = modelform_factory(
        model,
        form=form,
        fields=fields,
        exclude=exclude,
        formfield_callback=formfield_callback,
        widgets=widgets,
        localized_fields=localized_fields,
        labels=labels,
        help_texts=help_texts,
        error_messages=error_messages,
        field_classes=field_classes,
    )
    FormSet = formset_factory(
        form,
        formset,
        extra=extra,
        min_num=min_num,
        max_num=max_num,
        can_order=can_order,
        can_delete=can_delete,
        validate_min=validate_min,
        validate_max=validate_max,
        absolute_max=absolute_max,
        can_delete_extra=can_delete_extra,
        renderer=renderer,
    )
    FormSet.model = model
    FormSet.edit_only = edit_only
    return FormSet





class BaseInlineFormSet(BaseModelFormSet):
    

    def __init__(
        self,
        data=None,
        files=None,
        instance=None,
        save_as_new=False,
        prefix=None,
        queryset=None,
        **kwargs,
    ):
        if instance is None:
            self.instance = self.fk.remote_field.model()
        else:
            self.instance = instance
        self.save_as_new = save_as_new
        if queryset is None:
            queryset = self.model._default_manager
        if self.instance.pk is not None:
            qs = queryset.filter(**{self.fk.name: self.instance})
        else:
            qs = queryset.none()
        self.unique_fields = {self.fk.name}
        super().__init__(data, files, prefix=prefix, queryset=qs, **kwargs)

        
        
        if self.form._meta.fields and self.fk.name not in self.form._meta.fields:
            if isinstance(self.form._meta.fields, tuple):
                self.form._meta.fields = list(self.form._meta.fields)
            self.form._meta.fields.append(self.fk.name)

    def initial_form_count(self):
        if self.save_as_new:
            return 0
        return super().initial_form_count()

    def _construct_form(self, i, **kwargs):
        form = super()._construct_form(i, **kwargs)
        if self.save_as_new:
            mutable = getattr(form.data, "_mutable", None)
            
            if mutable is not None:
                form.data._mutable = True
            
            
            form.data[form.add_prefix(self._pk_field.name)] = None
            
            form.data[form.add_prefix(self.fk.name)] = None
            if mutable is not None:
                form.data._mutable = mutable

        
        fk_value = self.instance.pk
        if self.fk.remote_field.field_name != self.fk.remote_field.model._meta.pk.name:
            fk_value = getattr(self.instance, self.fk.remote_field.field_name)
            fk_value = getattr(fk_value, "pk", fk_value)
        setattr(form.instance, self.fk.get_attname(), fk_value)
        return form

    @classmethod
    def get_default_prefix(cls):
        return cls.fk.remote_field.get_accessor_name(model=cls.model).replace("+", "")

    def save_new(self, form, commit=True):
        
        
        
        setattr(form.instance, self.fk.name, self.instance)
        return super().save_new(form, commit=commit)

    def add_fields(self, form, index):
        super().add_fields(form, index)
        if self._pk_field == self.fk:
            name = self._pk_field.name
            kwargs = {"pk_field": True}
        else:
            
            
            name = self.fk.name
            kwargs = {
                "label": getattr(
                    form.fields.get(name), "label", capfirst(self.fk.verbose_name)
                )
            }

        
        
        
        if self.fk.remote_field.field_name != self.fk.remote_field.model._meta.pk.name:
            kwargs["to_field"] = self.fk.remote_field.field_name

        
        
        if self.instance._state.adding:
            if kwargs.get("to_field") is not None:
                to_field = self.instance._meta.get_field(kwargs["to_field"])
            else:
                to_field = self.instance._meta.pk

            if to_field.has_default() and (
                
                
                to_field.attname == self.fk.remote_field.model._meta.pk.name
                or not form.data
            ):
                setattr(self.instance, to_field.attname, None)

        form.fields[name] = InlineForeignKeyField(self.instance, **kwargs)

    def get_unique_error_message(self, unique_check):
        unique_check = [field for field in unique_check if field != self.fk.name]
        return super().get_unique_error_message(unique_check)


def _get_foreign_key(parent_model, model, fk_name=None, can_fail=False):
    
    
    from django.db.models import ForeignKey

    opts = model._meta
    if fk_name:
        fks_to_parent = [f for f in opts.fields if f.name == fk_name]
        if len(fks_to_parent) == 1:
            fk = fks_to_parent[0]
            parent_list = parent_model._meta.get_parent_list()
            parent_list.append(parent_model)
            if (
                not isinstance(fk, ForeignKey)
                or (
                    
                    fk.remote_field.model._meta.proxy
                    and fk.remote_field.model._meta.proxy_for_model not in parent_list
                )
                or (
                    
                    not fk.remote_field.model._meta.proxy
                    and fk.remote_field.model != parent_model
                    and fk.remote_field.model not in parent_list
                )
            ):
                raise ValueError(
                    "fk_name '%s' is not a ForeignKey to '%s'."
                    % (fk_name, parent_model._meta.label)
                )
        elif not fks_to_parent:
            raise ValueError(
                "'%s' has no field named '%s'." % (model._meta.label, fk_name)
            )
    else:
        
        parent_list = parent_model._meta.get_parent_list()
        parent_list.append(parent_model)
        fks_to_parent = [
            f
            for f in opts.fields
            if isinstance(f, ForeignKey)
            and (
                f.remote_field.model == parent_model
                or f.remote_field.model in parent_list
                or (
                    f.remote_field.model._meta.proxy
                    and f.remote_field.model._meta.proxy_for_model in parent_list
                )
            )
        ]
        if len(fks_to_parent) == 1:
            fk = fks_to_parent[0]
        elif not fks_to_parent:
            if can_fail:
                return
            raise ValueError(
                "'%s' has no ForeignKey to '%s'."
                % (
                    model._meta.label,
                    parent_model._meta.label,
                )
            )
        else:
            raise ValueError(
                "'%s' has more than one ForeignKey to '%s'. You must specify "
                "a 'fk_name' attribute."
                % (
                    model._meta.label,
                    parent_model._meta.label,
                )
            )
    return fk


def inlineformset_factory(
    parent_model,
    model,
    form=ModelForm,
    formset=BaseInlineFormSet,
    fk_name=None,
    fields=None,
    exclude=None,
    extra=3,
    can_order=False,
    can_delete=True,
    max_num=None,
    formfield_callback=None,
    widgets=None,
    validate_max=False,
    localized_fields=None,
    labels=None,
    help_texts=None,
    error_messages=None,
    min_num=None,
    validate_min=False,
    field_classes=None,
    absolute_max=None,
    can_delete_extra=True,
    renderer=None,
    edit_only=False,
):
    
    fk = _get_foreign_key(parent_model, model, fk_name=fk_name)
    
    if fk.unique:
        max_num = 1
    kwargs = {
        "form": form,
        "formfield_callback": formfield_callback,
        "formset": formset,
        "extra": extra,
        "can_delete": can_delete,
        "can_order": can_order,
        "fields": fields,
        "exclude": exclude,
        "min_num": min_num,
        "max_num": max_num,
        "widgets": widgets,
        "validate_min": validate_min,
        "validate_max": validate_max,
        "localized_fields": localized_fields,
        "labels": labels,
        "help_texts": help_texts,
        "error_messages": error_messages,
        "field_classes": field_classes,
        "absolute_max": absolute_max,
        "can_delete_extra": can_delete_extra,
        "renderer": renderer,
        "edit_only": edit_only,
    }
    FormSet = modelformset_factory(model, **kwargs)
    FormSet.fk = fk
    return FormSet





class InlineForeignKeyField(Field):
    

    widget = HiddenInput
    default_error_messages = {
        "invalid_choice": _("The inline value did not match the parent instance."),
    }

    def __init__(self, parent_instance, *args, pk_field=False, to_field=None, **kwargs):
        self.parent_instance = parent_instance
        self.pk_field = pk_field
        self.to_field = to_field
        if self.parent_instance is not None:
            if self.to_field:
                kwargs["initial"] = getattr(self.parent_instance, self.to_field)
            else:
                kwargs["initial"] = self.parent_instance.pk
        kwargs["required"] = False
        super().__init__(*args, **kwargs)

    def clean(self, value):
        if value in self.empty_values:
            if self.pk_field:
                return None
            
            return self.parent_instance
        
        if self.to_field:
            orig = getattr(self.parent_instance, self.to_field)
        else:
            orig = self.parent_instance.pk
        if str(value) != str(orig):
            raise ValidationError(
                self.error_messages["invalid_choice"], code="invalid_choice"
            )
        return self.parent_instance

    def has_changed(self, initial, data):
        return False


class ModelChoiceIteratorValue:
    def __init__(self, value, instance):
        self.value = value
        self.instance = instance

    def __str__(self):
        return str(self.value)

    def __hash__(self):
        return hash(self.value)

    def __eq__(self, other):
        if isinstance(other, ModelChoiceIteratorValue):
            other = other.value
        return self.value == other


class ModelChoiceIterator(BaseChoiceIterator):
    def __init__(self, field):
        self.field = field
        self.queryset = field.queryset

    def __iter__(self):
        if self.field.empty_label is not None:
            yield ("", self.field.empty_label)
        queryset = self.queryset
        
        if not queryset._prefetch_related_lookups:
            queryset = queryset.iterator()
        for obj in queryset:
            yield self.choice(obj)

    def __len__(self):
        
        
        
        return self.queryset.count() + (1 if self.field.empty_label is not None else 0)

    def __bool__(self):
        return self.field.empty_label is not None or self.queryset.exists()

    def choice(self, obj):
        return (
            ModelChoiceIteratorValue(self.field.prepare_value(obj), obj),
            self.field.label_from_instance(obj),
        )


class ModelChoiceField(ChoiceField):
    

    
    
    default_error_messages = {
        "invalid_choice": _(
            "Select a valid choice. That choice is not one of the available choices."
        ),
    }
    iterator = ModelChoiceIterator

    def __init__(
        self,
        queryset,
        *,
        empty_label="---------",
        required=True,
        widget=None,
        label=None,
        initial=None,
        help_text="",
        to_field_name=None,
        limit_choices_to=None,
        blank=False,
        **kwargs,
    ):
        
        
        Field.__init__(
            self,
            required=required,
            widget=widget,
            label=label,
            initial=initial,
            help_text=help_text,
            **kwargs,
        )
        if (required and initial is not None) or (
            isinstance(self.widget, RadioSelect) and not blank
        ):
            self.empty_label = None
        else:
            self.empty_label = empty_label
        self.queryset = queryset
        self.limit_choices_to = limit_choices_to  
        self.to_field_name = to_field_name

    def get_limit_choices_to(self):
        
        if callable(self.limit_choices_to):
            return self.limit_choices_to()
        return self.limit_choices_to

    def __deepcopy__(self, memo):
        result = super(ChoiceField, self).__deepcopy__(memo)
        
        if self.queryset is not None:
            result.queryset = self.queryset.all()
        return result

    def _get_queryset(self):
        return self._queryset

    def _set_queryset(self, queryset):
        self._queryset = None if queryset is None else queryset.all()
        self.widget.choices = self.choices

    queryset = property(_get_queryset, _set_queryset)

    
    
    def label_from_instance(self, obj):
        
        return str(obj)

    def _get_choices(self):
        
        
        if hasattr(self, "_choices"):
            return self._choices

        
        
        
        
        
        
        
        return self.iterator(self)

    choices = property(_get_choices, ChoiceField.choices.fset)

    def prepare_value(self, value):
        if hasattr(value, "_meta"):
            if self.to_field_name:
                return value.serializable_value(self.to_field_name)
            else:
                return value.pk
        return super().prepare_value(value)

    def to_python(self, value):
        if value in self.empty_values:
            return None
        try:
            key = self.to_field_name or "pk"
            if isinstance(value, self.queryset.model):
                value = getattr(value, key)
            value = self.queryset.get(**{key: value})
        except (ValueError, TypeError, self.queryset.model.DoesNotExist):
            raise ValidationError(
                self.error_messages["invalid_choice"],
                code="invalid_choice",
                params={"value": value},
            )
        return value

    def validate(self, value):
        return Field.validate(self, value)

    def has_changed(self, initial, data):
        if self.disabled:
            return False
        initial_value = initial if initial is not None else ""
        data_value = data if data is not None else ""
        return str(self.prepare_value(initial_value)) != str(data_value)


class ModelMultipleChoiceField(ModelChoiceField):
    

    widget = SelectMultiple
    hidden_widget = MultipleHiddenInput
    default_error_messages = {
        "invalid_list": _("Enter a list of values."),
        "invalid_choice": _(
            "Select a valid choice. %(value)s is not one of the available choices."
        ),
        "invalid_pk_value": _("“%(pk)s” is not a valid value."),
    }

    def __init__(self, queryset, **kwargs):
        super().__init__(queryset, empty_label=None, **kwargs)

    def to_python(self, value):
        if not value:
            return []
        return list(self._check_values(value))

    def clean(self, value):
        value = self.prepare_value(value)
        if self.required and not value:
            raise ValidationError(self.error_messages["required"], code="required")
        elif not self.required and not value:
            return self.queryset.none()
        if not isinstance(value, (list, tuple)):
            raise ValidationError(
                self.error_messages["invalid_list"],
                code="invalid_list",
            )
        qs = self._check_values(value)
        
        
        self.run_validators(value)
        return qs

    def _check_values(self, value):
        
        key = self.to_field_name or "pk"
        
        
        try:
            value = frozenset(value)
        except TypeError:
            
            raise ValidationError(
                self.error_messages["invalid_list"],
                code="invalid_list",
            )
        for pk in value:
            try:
                self.queryset.filter(**{key: pk})
            except (ValueError, TypeError):
                raise ValidationError(
                    self.error_messages["invalid_pk_value"],
                    code="invalid_pk_value",
                    params={"pk": pk},
                )
        qs = self.queryset.filter(**{"%s__in" % key: value})
        pks = {str(getattr(o, key)) for o in qs}
        for val in value:
            if str(val) not in pks:
                raise ValidationError(
                    self.error_messages["invalid_choice"],
                    code="invalid_choice",
                    params={"value": val},
                )
        return qs

    def prepare_value(self, value):
        if (
            hasattr(value, "__iter__")
            and not isinstance(value, str)
            and not hasattr(value, "_meta")
        ):
            prepare_value = super().prepare_value
            return [prepare_value(v) for v in value]
        return super().prepare_value(value)

    def has_changed(self, initial, data):
        if self.disabled:
            return False
        if initial is None:
            initial = []
        if data is None:
            data = []
        if len(initial) != len(data):
            return True
        initial_set = {str(value) for value in self.prepare_value(initial)}
        data_set = {str(value) for value in data}
        return data_set != initial_set


def modelform_defines_fields(form_class):
    return hasattr(form_class, "_meta") and (
        form_class._meta.fields is not None or form_class._meta.exclude is not None
    )