take a pair of projectstates and compare them to see what the first would need doing to make it match the second the second usually being the project s current state note that this naturally operates on entire projects at a time as it s likely that changes interact for example you can t add a foreignkey without having a migration to add the table it depends on first a user interface may offer singleapp usage if it wishes with the caveat that it may not always be possible main entry point to produce a list of applicable changes take a graph to base names on and an optional set of apps to try and restrict to restriction is not guaranteed recursive deconstruction for a field and its arguments used for full comparison for renamealter sometimes a singlelevel deconstruction will not compare correctly if this is a type that implements deconstruct as an instance method avoid treating this as being deconstructible itself see 22951 we have a field which also returns a name return a definition of the fields that ignores field names and what related fields actually relate to used for detecting renames as the related fields change during renames return a dict of migration plans which will achieve the change from fromstate to tostate the dict has app labels as keys and a list of migrations as values the resulting migrations aren t specially named but the names do matter for dependencies inside the set convertapps is the list of apps to convert to use migrations i e to make initial migrations for in the usual case graph is an optional argument that if provided can help improve dependency generation and avoid potential circular dependencies the first phase is generating all the operations for each app and gathering them into a big perapp list then go through that list order it and split into migrations to resolve dependencies caused by m2ms and fks prepare some oldnew state and model lists separating proxy models and ignoring unmigrated apps renames have to come first prepare lists of fields and generate through model map generate nonrename model operations create the renamed fields and store them in self renamedfields they are used by createalteredindexes generatealteredfields generateremovedalteredindexuniquetogether and generatealteredindexuniquetogether create the altered indexes and store them in self alteredindexes this avoids the same computation in generateremovedindexes and generateaddedindexes generate index removal operations before field is removed generate field renaming operations generate removal of foo together generate field operations prepare field lists and a list of the fields that used through models in the old state so dependencies can be made from the through model deletion to the field that uses it through model map generation for applabel modelname in sortedself oldmodelkeys oldmodelname self renamedmodels get applabel modelname modelname oldmodelstate self fromstate modelsapplabel oldmodelname for fieldname field in oldmodelstate fields items if hasattrfield remotefield and getattr field remotefield through none throughkey resolverelation field remotefield through applabel modelname self throughusersthroughkey applabel oldmodelname fieldname staticmethod def resolvedependencydependency if dependency applabel setting return dependency false resolvedapplabel resolvedobjectname getattr settings dependency modelname split return operationdependency resolvedapplabel resolvedobjectname lower dependency fieldname dependency type true def buildmigrationlistself graphnone self migrations numops sumlenx for x in self generatedoperations values chopmode false while numops on every iteration we step through all the apps and see if there is a completed set of operations if we find that a subset of the operations are complete we can try to chop it off from the rest and continue but we only do this if we ve already been through the list once before without any chopping and nothing has changed for applabel in sortedself generatedoperations chopped dependencies set for operation in listself generatedoperationsapplabel depssatisfied true operationdependencies set for dep in operation autodeps temporarily resolve the swappable dependency to prevent circular references while keeping the dependency checks on the resolved model add the swappable dependencies originaldep dep dep isswappabledep self resolvedependencydep if dep applabel applabel external app dependency see if it s not yet satisfied for otheroperation in self generatedoperations get dep applabel if self checkdependencyotheroperation dep depssatisfied false break if not depssatisfied break else if isswappabledep operationdependencies add originaldep applabel originaldep modelname elif dep applabel in self migrations operationdependencies add dep applabel self migrationsdep applabel1 name else if we can t find the other app we add a firstlast dependency but only if we ve already been through once and checked everything if chopmode if the app already exists we add a dependency on the last migration as we don t know which migration contains the target field if it s not yet migrated or has no migrations we use first if graph and graph leafnodesdep applabel operationdependencies add graph leafnodesdep applabel0 else operationdependencies add dep applabel first else depssatisfied false if depssatisfied chopped appendoperation dependencies updateoperationdependencies del self generatedoperationsapplabel0 else break make a migration well only if there s stuff to put in it if dependencies or chopped if not self generatedoperationsapplabel or chopmode subclass type migration migration operations dependencies instance subclass autoi lenself migrations getapplabel 1 applabel instance dependencies listdependencies instance operations chopped instance initial applabel not in self existingapps self migrations setdefaultapplabel appendinstance chopmode false else self generatedoperationsapplabel chopped self generatedoperationsapplabel newnumops sumlenx for x in self generatedoperations values if newnumops numops if not chopmode chopmode true else raise valueerror cannot resolve operation dependencies r self generatedoperations numops newnumops def sortmigrationsself for applabel ops in sortedself generatedoperations items ts topologicalsorter for op in ops ts addop for dep in op autodeps resolve intraapp dependencies to handle circular references involving a swappable model dep self resolvedependencydep0 if dep applabel applabel continue ts addop x for x in ops if self checkdependencyx dep self generatedoperationsapplabel listts staticorder def optimizemigrationsself add in internal dependencies among the migrations for applabel migrations in self migrations items for m1 m2 in zipmigrations migrations1 m2 dependencies appendapplabel m1 name dedupe dependencies for migrations in self migrations values for migration in migrations migration dependencies listsetmigration dependencies optimize migrations for applabel migrations in self migrations items for migration in migrations migration operations migrationoptimizer optimize migration operations applabel def checkdependencyself operation dependency created model if dependency fieldname is none and dependency type operationdependency type create return isinstanceoperation operations createmodel and operation namelower dependency modelnamelower created field elif dependency fieldname is not none and dependency type operationdependency type create return isinstanceoperation operations createmodel and operation namelower dependency modelnamelower and anydependency fieldname x for x y in operation fields or isinstanceoperation operations addfield and operation modelnamelower dependency modelnamelower and operation namelower dependency fieldnamelower removed field elif dependency fieldname is not none and dependency type operationdependency type remove return isinstanceoperation operations removefield and operation modelnamelower dependency modelnamelower and operation namelower dependency fieldnamelower removed model elif dependency fieldname is none and dependency type operationdependency type remove return isinstanceoperation operations deletemodel and operation namelower dependency modelnamelower field being altered elif dependency fieldname is not none and dependency type operationdependency type alter return isinstanceoperation operations alterfield and operation modelnamelower dependency modelnamelower and operation namelower dependency fieldnamelower orderwithrespectto being unset for a field elif dependency fieldname is not none and dependency type operationdependency type removeorderwrt return isinstanceoperation operations alterorderwithrespectto and operation namelower dependency modelnamelower and operation orderwithrespectto or lower dependency fieldnamelower field is removed and part of an indexuniquetogether elif dependency fieldname is not none and dependency type operationdependency type alterfootogether return isinstance operation operations alteruniquetogether operations alterindextogether and operation namelower dependency modelnamelower unknown dependency raise an error else raise valueerrorcan t handle dependency r dependency def addoperationself applabel operation dependenciesnone beginningfalse dependencies are applabel modelname fieldname createdelete as truefalse operation autodeps dependencies or if beginning self generatedoperations setdefaultapplabel insert0 operation else self generatedoperations setdefaultapplabel appendoperation def swappablefirstkeyself item try modelstate self tostate modelsitem basenames base if isinstancebase str else base name for base in modelstate bases stringversion s s item0 item1 if modelstate options getswappable or abstractuser in basenames or abstractbaseuser in basenames or settings authusermodel lower stringversion lower return item0 item1 except lookuperror pass return item def generaterenamedmodelsself self renamedmodels self renamedmodelsrel addedmodels self newmodelkeys self oldmodelkeys for applabel modelname in sortedaddedmodels modelstate self tostate modelsapplabel modelname modelfieldsdef self onlyrelationagnosticfieldsmodelstate fields removedmodels self oldmodelkeys self newmodelkeys for remapplabel remmodelname in removedmodels if remapplabel applabel remmodelstate self fromstate models remapplabel remmodelname remmodelfieldsdef self onlyrelationagnosticfields remmodelstate fields if modelfieldsdef remmodelfieldsdef if self questioner askrenamemodel remmodelstate modelstate dependencies fields listmodelstate fields values field remotefield for relations in self tostate relations applabel modelname values for field in relations values for field in fields if field isrelation dependencies extend self getdependenciesforforeignkey applabel modelname field self tostate self addoperation applabel operations renamemodel oldnameremmodelstate name newnamemodelstate name dependenciesdependencies self renamedmodelsapplabel modelname remmodelname renamedmodelsrelkey s s remmodelstate applabel remmodelstate namelower self renamedmodelsrel renamedmodelsrelkey s s modelstate applabel modelstate namelower self oldmodelkeys removeremapplabel remmodelname self oldmodelkeys addapplabel modelname break def generatecreatedmodelsself oldkeys self oldmodelkeys self oldunmanagedkeys addedmodels self newmodelkeys oldkeys addedunmanagedmodels self newunmanagedkeys oldkeys alladdedmodels chain sortedaddedmodels keyself swappablefirstkey reversetrue sortedaddedunmanagedmodels keyself swappablefirstkey reversetrue for applabel modelname in alladdedmodels modelstate self tostate modelsapplabel modelname gather related fields relatedfields primarykeyrel none for fieldname field in modelstate fields items if field remotefield if field remotefield model if field primarykey primarykeyrel field remotefield model elif not field remotefield parentlink relatedfieldsfieldname field if getattrfield remotefield through none relatedfieldsfieldname field are there indexesuniquetogether to defer indexes modelstate options popindexes constraints modelstate options popconstraints uniquetogether modelstate options popuniquetogether none orderwithrespectto modelstate options pop orderwithrespectto none depend on the deletion of any possible proxy version of us dependencies operationdependency applabel modelname none operationdependency type remove depend on all bases for base in modelstate bases if isinstancebase str and in base baseapplabel basename base split 1 dependencies append operationdependency baseapplabel basename none operationdependency type create depend on the removal of base fields if the new model has a field with the same name oldbasemodelstate self fromstate models get baseapplabel basename newbasemodelstate self tostate models get baseapplabel basename if oldbasemodelstate and newbasemodelstate removedbasefields setoldbasemodelstate fields difference newbasemodelstate fields intersectionmodelstate fields for removedbasefield in removedbasefields dependencies append operationdependency baseapplabel basename removedbasefield operationdependency type remove depend on the other end of the primary key if it s a relation if primarykeyrel dependencies append operationdependency resolverelationprimarykeyrel applabel modelname none operationdependency type create generate creation operation self addoperation applabel operations createmodel namemodelstate name fields d for d in modelstate fields items if d0 not in relatedfields optionsmodelstate options basesmodelstate bases managersmodelstate managers dependenciesdependencies beginningtrue don t add operations which modify the database for unmanaged models if not modelstate options getmanaged true continue generate operations for each related field for name field in sortedrelatedfields items dependencies self getdependenciesforforeignkey applabel modelname field self tostate depend on our own model being created dependencies append operationdependency applabel modelname none operationdependency type create make operation self addoperation applabel operations addfield modelnamemodelname namename fieldfield dependencieslistsetdependencies generate other opns if orderwithrespectto self addoperation applabel operations alterorderwithrespectto namemodelname orderwithrespecttoorderwithrespectto dependencies operationdependency applabel modelname orderwithrespectto operationdependency type create operationdependency applabel modelname none operationdependency type create relateddependencies operationdependency applabel modelname name operationdependency type create for name in sortedrelatedfields relateddependencies append operationdependency applabel modelname none operationdependency type create for index in indexes self addoperation applabel operations addindex modelnamemodelname indexindex dependenciesrelateddependencies for constraint in constraints self addoperation applabel operations addconstraint modelnamemodelname constraintconstraint dependenciesrelateddependencies if uniquetogether self addoperation applabel operations alteruniquetogether namemodelname uniquetogetheruniquetogether dependenciesrelateddependencies fix relationships if the model changed from a proxy model to a concrete model relations self tostate relations if applabel modelname in self oldproxykeys for relatedmodelkey relatedfields in relations applabel modelname items relatedmodelstate self tostate modelsrelatedmodelkey for relatedfieldname relatedfield in relatedfields items self addoperation relatedmodelstate applabel operations alterfield modelnamerelatedmodelstate name namerelatedfieldname fieldrelatedfield dependencies operationdependency applabel modelname none operationdependency type create def generatecreatedproxiesself added self newproxykeys self oldproxykeys for applabel modelname in sortedadded modelstate self tostate modelsapplabel modelname assert modelstate options getproxy depend on the deletion of any possible nonproxy version of us dependencies operationdependency applabel modelname none operationdependency type remove depend on all bases for base in modelstate bases if isinstancebase str and in base baseapplabel basename base split 1 dependencies append operationdependency baseapplabel basename none operationdependency type create generate creation operation self addoperation applabel operations createmodel namemodelstate name fields optionsmodelstate options basesmodelstate bases managersmodelstate managers depend on the deletion of any possible nonproxy version of us dependenciesdependencies def generatedeletedmodelsself newkeys self newmodelkeys self newunmanagedkeys deletedmodels self oldmodelkeys newkeys deletedunmanagedmodels self oldunmanagedkeys newkeys alldeletedmodels chain sorteddeletedmodels sorteddeletedunmanagedmodels for applabel modelname in alldeletedmodels modelstate self fromstate modelsapplabel modelname gather related fields relatedfields for fieldname field in modelstate fields items if field remotefield if field remotefield model relatedfieldsfieldname field if getattrfield remotefield through none relatedfieldsfieldname field generate option removal first uniquetogether modelstate options popuniquetogether none if uniquetogether self addoperation applabel operations alteruniquetogether namemodelname uniquetogethernone then remove each related field for name in sortedrelatedfields self addoperation applabel operations removefield modelnamemodelname namename finally remove the model this depends on both the removalalteration of all incoming fields and the removal of all its own related fields and if it s a through model the field that references it dependencies relations self fromstate relations for relatedobjectapplabel objectname relationrelatedfields in relationsapplabel modelname items for fieldname field in relationrelatedfields items dependencies append operationdependency relatedobjectapplabel objectname fieldname operationdependency type remove if not field manytomany dependencies append operationdependency relatedobjectapplabel objectname fieldname operationdependency type alter for name in sortedrelatedfields dependencies append operationdependency applabel modelname name operationdependency type remove we re referenced in another field s through throughuser self throughusers getapplabel modelstate namelower if throughuser dependencies append operationdependencythroughuser operationdependency type remove finally make the operation deduping any dependencies self addoperation applabel operations deletemodel namemodelstate name dependencieslistsetdependencies def generatedeletedproxiesself work out renamed fields self renamedoperations oldfieldkeys self oldfieldkeys copy for applabel modelname fieldname in sorted self newfieldkeys oldfieldkeys oldmodelname self renamedmodels get applabel modelname modelname oldmodelstate self fromstate modelsapplabel oldmodelname newmodelstate self tostate modelsapplabel modelname field newmodelstate getfieldfieldname scan to see if this is actually a rename fielddec self deepdeconstructfield for remapplabel remmodelname remfieldname in sorted oldfieldkeys self newfieldkeys if remapplabel applabel and remmodelname modelname oldfield oldmodelstate getfieldremfieldname oldfielddec self deepdeconstructoldfield if field remotefield and field remotefield model and to in oldfielddec2 oldrelto oldfielddec2to if oldrelto in self renamedmodelsrel oldfielddec2to self renamedmodelsreloldrelto oldfield setattributesfromnameremfieldname olddbcolumn oldfield getattnamecolumn1 if oldfielddec fielddec or was the field renamed and dbcolumn equal to the old field s column added oldfielddec0 2 fielddec0 2 and dictoldfielddec2 dbcolumnolddbcolumn fielddec2 if self questioner askrename modelname remfieldname fieldname field self renamedoperations append remapplabel remmodelname oldfield dbcolumn remfieldname applabel modelname field fieldname oldfieldkeys remove remapplabel remmodelname remfieldname oldfieldkeys addapplabel modelname fieldname self renamedfields applabel modelname fieldname remfieldname break def generaterenamedfieldsself a dbcolumn mismatch requires a prior noop alterfield for the subsequent renamefield to be a noop on attempts at preserving the old name make addfield operations for applabel modelname fieldname in sorted self newfieldkeys self oldfieldkeys self generateaddedfieldapplabel modelname fieldname def generateaddedfieldself applabel modelname fieldname field self tostate modelsapplabel modelname getfieldfieldname adding a field always depends at least on its removal dependencies operationdependency applabel modelname fieldname operationdependency type remove fields that are foreignkeysm2ms depend on stuff if field remotefield and field remotefield model dependencies extend self getdependenciesforforeignkey applabel modelname field self tostate you can t just add not null fields with no default or fields which don t allow empty strings as default timefields models datefield models datetimefield models timefield preservedefault field null or field hasdefault or field dbdefault is not models notprovided or field manytomany or field blank and field emptystringsallowed or isinstancefield timefields and field autonow if not preservedefault field field clone if isinstancefield timefields and field autonowadd field default self questioner askautonowaddaddition fieldname modelname else field default self questioner asknotnulladdition fieldname modelname if field unique and field default is not models notprovided and callablefield default self questioner askuniquecallabledefaultadditionfieldname modelname self addoperation applabel operations addfield modelnamemodelname namefieldname fieldfield preservedefaultpreservedefault dependenciesdependencies def generateremovedfieldsself we might need to depend on the removal of an orderwithrespectto or indexuniquetogether operation this is safely ignored if there isn t one make alterfield operations or possibly removedfieldaddfield if alter isn t possible did the field change implement any model renames on relations these are handled by renamemodel so we need to exclude them from the comparison handle foreignkey which can only have a single tofield repoint both model and field name because tofield inclusion in foreignkey deconstruct is based on both handle foreignobjects which can have multiple fromfieldstofields if the field was confirmed to be renamed it means that only dbcolumn was allowed to change which generaterenamedfields already accounts for by adding an alterfield operation either both fields are m2m or neither is we cannot alter between m2m and concrete fields find renamed indexes indexes are the same except for the names find indextogether changed to indexes ensure only fields are defined in the index remove renamed indexes from the lists of added and removed indexes account for fks to swappable models return foreign key dependencies of the given model dependencies modelstate self tostate modelsapplabel modelname for field in modelstate fields values if field isrelation dependencies extend self getdependenciesforforeignkey applabel modelname field self tostate return dependencies def getalteredfootogetheroperationsself optionname for applabel modelname in sortedself keptmodelkeys oldmodelname self renamedmodels get applabel modelname modelname oldmodelstate self fromstate modelsapplabel oldmodelname newmodelstate self tostate modelsapplabel modelname we run the old version through the field renames to account for those oldvalue oldmodelstate options getoptionname oldvalue tuple self renamedfields getapplabel modelname n n for n in unique for unique in oldvalue if oldvalue else set newvalue newmodelstate options getoptionname newvalue setnewvalue if newvalue else set if oldvalue newvalue dependencies for footogethers in newvalue for fieldname in footogethers field newmodelstate getfieldfieldname if field remotefield and field remotefield model dependencies extend self getdependenciesforforeignkey applabel modelname field self tostate yield oldvalue newvalue applabel modelname dependencies def generateremovedalteredfootogetherself operation for oldvalue newvalue applabel modelname dependencies in self getalteredfootogetheroperationsoperation optionname if operation operations alterindextogether oldvalue value for value in oldvalue if value not in self renamedindextogethervaluesapplabel modelname removalvalue newvalue intersectionoldvalue if removalvalue or oldvalue self addoperation applabel operation namemodelname operation optionname removalvalue dependenciesdependencies def generateremovedaltereduniquetogetherself self generateremovedalteredfootogetheroperations alteruniquetogether def generatealteredfootogetherself operation for oldvalue newvalue applabel modelname dependencies in self getalteredfootogetheroperationsoperation optionname removalvalue newvalue intersectionoldvalue if newvalue removalvalue self addoperation applabel operationnamemodelname operation optionname newvalue dependenciesdependencies def generatealtereduniquetogetherself self generatealteredfootogetheroperations alteruniquetogether def generatealtereddbtableself modelstocheck self keptmodelkeys union self keptproxykeys self keptunmanagedkeys for applabel modelname in sortedmodelstocheck oldmodelname self renamedmodels get applabel modelname modelname oldmodelstate self fromstate modelsapplabel oldmodelname newmodelstate self tostate modelsapplabel modelname olddbtablename oldmodelstate options getdbtable newdbtablename newmodelstate options getdbtable if olddbtablename newdbtablename self addoperation applabel operations altermodeltable namemodelname tablenewdbtablename def generatealtereddbtablecommentself modelstocheck self keptmodelkeys union self keptproxykeys self keptunmanagedkeys for applabel modelname in sortedmodelstocheck oldmodelname self renamedmodels get applabel modelname modelname oldmodelstate self fromstate modelsapplabel oldmodelname newmodelstate self tostate modelsapplabel modelname olddbtablecomment oldmodelstate options getdbtablecomment newdbtablecomment newmodelstate options getdbtablecomment if olddbtablecomment newdbtablecomment self addoperation applabel operations altermodeltablecomment namemodelname tablecommentnewdbtablecomment def generatealteredoptionsself modelstocheck self keptmodelkeys union self keptproxykeys self keptunmanagedkeys unmanaged converted to managed self oldunmanagedkeys self newmodelkeys managed converted to unmanaged self oldmodelkeys self newunmanagedkeys for applabel modelname in sortedmodelstocheck oldmodelname self renamedmodels get applabel modelname modelname oldmodelstate self fromstate modelsapplabel oldmodelname newmodelstate self tostate modelsapplabel modelname oldoptions key value for key value in oldmodelstate options items if key in altermodeloptions alteroptionkeys newoptions key value for key value in newmodelstate options items if key in altermodeloptions alteroptionkeys if oldoptions newoptions self addoperation applabel operations altermodeloptions namemodelname optionsnewoptions def generatealteredorderwithrespecttoself for applabel modelname in sortedself keptmodelkeys oldmodelname self renamedmodels get applabel modelname modelname oldmodelstate self fromstate modelsapplabel oldmodelname newmodelstate self tostate modelsapplabel modelname if oldmodelstate options get orderwithrespectto newmodelstate options getorderwithrespectto make sure it comes second if we re adding removal dependency is part of removefield dependencies if newmodelstate options getorderwithrespectto dependencies append operationdependency applabel modelname newmodelstate optionsorderwithrespectto operationdependency type create actually generate the operation self addoperation applabel operations alterorderwithrespectto namemodelname orderwithrespecttonewmodelstate options get orderwithrespectto dependenciesdependencies def generatealteredmanagersself for applabel modelname in sortedself keptmodelkeys oldmodelname self renamedmodels get applabel modelname modelname oldmodelstate self fromstate modelsapplabel oldmodelname newmodelstate self tostate modelsapplabel modelname if oldmodelstate managers newmodelstate managers self addoperation applabel operations altermodelmanagers namemodelname managersnewmodelstate managers def arrangeforgraphself changes graph migrationnamenone leaves graph leafnodes namemap for applabel migrations in listchanges items if not migrations continue find the app label s current leaf node appleaf none for leaf in leaves if leaf0 applabel appleaf leaf break do they want an initial migration for this app if appleaf is none and not self questioner askinitialapplabel they don t for migration in migrations namemapapplabel migration name applabel first del changesapplabel continue work out the next number in the sequence if appleaf is none nextnumber 1 else nextnumber self parsenumberappleaf1 or 0 1 name each migration for i migration in enumeratemigrations if i 0 and appleaf migration dependencies appendappleaf newnameparts 04i nextnumber if migrationname newnameparts appendmigrationname elif i 0 and not appleaf newnameparts appendinitial else newnameparts appendmigration suggestname 100 newname joinnewnameparts namemapapplabel migration name applabel newname nextnumber 1 migration name newname now fix dependencies for migrations in changes values for migration in migrations migration dependencies namemap getd d for d in migration dependencies return changes def trimtoappsself changes applabels gather other app dependencies in a first pass appdependencies for applabel migrations in changes items for migration in migrations for depapplabel name in migration dependencies appdependencies setdefaultapplabel set adddepapplabel requiredapps setapplabels keep resolving till there s no change oldrequiredapps none while oldrequiredapps requiredapps oldrequiredapps setrequiredapps requiredapps update appdependencies getapplabel for applabel in requiredapps remove all migrations that aren t needed for applabel in listchanges if applabel not in requiredapps del changesapplabel return changes classmethod def parsenumbercls name if squashedmatch re searchr squashedd name return intsquashedmatch1 match re matchrd name if match return intmatch0 return none take a pair of projectstates and compare them to see what the first would need doing to make it match the second the second usually being the project s current state note that this naturally operates on entire projects at a time as it s likely that changes interact for example you can t add a foreignkey without having a migration to add the table it depends on first a user interface may offer single app usage if it wishes with the caveat that it may not always be possible main entry point to produce a list of applicable changes take a graph to base names on and an optional set of apps to try and restrict to restriction is not guaranteed recursive deconstruction for a field and its arguments used for full comparison for rename alter sometimes a single level deconstruction will not compare correctly if this is a type that implements deconstruct as an instance method avoid treating this as being deconstructible itself see 22951 we have a field which also returns a name return a definition of the fields that ignores field names and what related fields actually relate to used for detecting renames as the related fields change during renames return a dict of migration plans which will achieve the change from from_state to to_state the dict has app labels as keys and a list of migrations as values the resulting migrations aren t specially named but the names do matter for dependencies inside the set convert_apps is the list of apps to convert to use migrations i e to make initial migrations for in the usual case graph is an optional argument that if provided can help improve dependency generation and avoid potential circular dependencies the first phase is generating all the operations for each app and gathering them into a big per app list then go through that list order it and split into migrations to resolve dependencies caused by m2ms and fks prepare some old new state and model lists separating proxy models and ignoring unmigrated apps renames have to come first prepare lists of fields and generate through model map generate non rename model operations create the renamed fields and store them in self renamed_fields they are used by create_altered_indexes generate_altered_fields generate_removed_altered_index unique_together and generate_altered_index unique_together create the altered indexes and store them in self altered_indexes this avoids the same computation in generate_removed_indexes and generate_added_indexes generate index removal operations before field is removed generate field renaming operations generate removal of foo together generate field operations prepare field lists and a list of the fields that used through models in the old state so dependencies can be made from the through model deletion to the field that uses it through model map generation return the resolved dependency and a boolean denoting whether or not it was swappable chop the lists of operations up into migrations with dependencies on each other do this by going through an app s list of operations until one is found that has an outgoing dependency that isn t in another app s migration yet hasn t been chopped off its list then chop off the operations before it into a migration and move onto the next app if the loops completes without doing anything there s a circular dependency which _should_ be impossible as the operations are all split at this point so they can t depend and be depended on on every iteration we step through all the apps and see if there is a completed set of operations if we find that a subset of the operations are complete we can try to chop it off from the rest and continue but we only do this if we ve already been through the list once before without any chopping and nothing has changed temporarily resolve the swappable dependency to prevent circular references while keeping the dependency checks on the resolved model add the swappable dependencies external app dependency see if it s not yet satisfied if we can t find the other app we add a first last dependency but only if we ve already been through once and checked everything if the app already exists we add a dependency on the last migration as we don t know which migration contains the target field if it s not yet migrated or has no migrations we use __first__ make a migration well only if there s stuff to put in it reorder to make things possible reordering may be needed so fks work nicely inside the same app resolve intra app dependencies to handle circular references involving a swappable model add in internal dependencies among the migrations de dupe dependencies optimize migrations return true if the given operation depends on the given dependency false otherwise created model created field removed field removed model field being altered order_with_respect_to being unset for a field field is removed and part of an index unique_together unknown dependency raise an error dependencies are app_label model_name field_name create delete as true false place potential swappable models first in lists of created models only real way to solve 22783 find any renamed models generate the operations for them and remove the old entry from the model lists must be run before other model level generation find all new models both managed and unmanaged and make create operations for them as well as separate operations to create any foreign key or m2m relationships these are optimized later if possible defer any model options that refer to collections of fields that might be deferred e g unique_together gather related fields are there indexes unique_together to defer depend on the deletion of any possible proxy version of us depend on all bases depend on the removal of base fields if the new model has a field with the same name depend on the other end of the primary key if it s a relation generate creation operation don t add operations which modify the database for unmanaged models generate operations for each related field depend on our own model being created make operation generate other opns fix relationships if the model changed from a proxy model to a concrete model make createmodel statements for proxy models use the same statements as that way there s less code duplication but for proxy models it s safe to skip all the pointless field stuff and chuck out an operation depend on the deletion of any possible non proxy version of us depend on all bases generate creation operation depend on the deletion of any possible non proxy version of us find all deleted models managed and unmanaged and make delete operations for them as well as separate operations to delete any foreign key or m2m relationships these are optimized later if possible also bring forward removal of any model options that refer to collections of fields the inverse of generate_created_models gather related fields generate option removal first then remove each related field finally remove the model this depends on both the removal alteration of all incoming fields and the removal of all its own related fields and if it s a through model the field that references it we re referenced in another field s through finally make the operation deduping any dependencies make deletemodel options for proxy models work out renamed fields scan to see if this is actually a rename was the field renamed and db_column equal to the old field s column added generate renamefield operations a db_column mismatch requires a prior noop alterfield for the subsequent renamefield to be a noop on attempts at preserving the old name make addfield operations adding a field always depends at least on its removal fields that are foreignkeys m2ms depend on stuff you can t just add not null fields with no default or fields which don t allow empty strings as default make removefield operations we might need to depend on the removal of an order_with_respect_to or index unique_together operation this is safely ignored if there isn t one make alterfield operations or possibly removedfield addfield if alter isn t possible did the field change implement any model renames on relations these are handled by renamemodel so we need to exclude them from the comparison handle foreignkey which can only have a single to_field repoint both model and field name because to_field inclusion in foreignkey deconstruct is based on both handle foreignobjects which can have multiple from_fields to_fields if the field was confirmed to be renamed it means that only db_column was allowed to change which generate_renamed_fields already accounts for by adding an alterfield operation either both fields are m2m or neither is we cannot alter between m2m and concrete fields find renamed indexes indexes are the same except for the names find index_together changed to indexes ensure only fields are defined in the index remove renamed indexes from the lists of added and removed indexes account for fks to swappable models return foreign key dependencies of the given model we run the old version through the field renames to account for those work out if any non schema affecting options have changed and make an operation to represent them in state changes in case python code in migrations needs them unmanaged converted to managed managed converted to unmanaged make sure it comes second if we re adding removal dependency is part of removefield actually generate the operation take a result from changes and a migrationgraph and fix the names and dependencies of the changes so they extend the graph from the leaf nodes for each app find the app label s current leaf node do they want an initial migration for this app they don t work out the next number in the sequence name each migration now fix dependencies take changes from arrange_for_graph and set of app labels and return a modified set of changes which trims out as many migrations that are not in app_labels as possible note that some other migrations may still be present as they may be required dependencies gather other app dependencies in a first pass keep resolving till there s no change remove all migrations that aren t needed given a migration name try to extract a number from the beginning of it for a squashed migration such as 0001_squashed_0004 return the second number if no number is found return none
import functools
import re
from collections import defaultdict, namedtuple
from enum import Enum
from graphlib import TopologicalSorter
from itertools import chain

from django.conf import settings
from django.db import models
from django.db.migrations import operations
from django.db.migrations.migration import Migration
from django.db.migrations.operations.models import AlterModelOptions
from django.db.migrations.optimizer import MigrationOptimizer
from django.db.migrations.questioner import MigrationQuestioner
from django.db.migrations.utils import (
    COMPILED_REGEX_TYPE,
    RegexObject,
    resolve_relation,
)
from django.utils.functional import cached_property


class OperationDependency(
    namedtuple("OperationDependency", "app_label model_name field_name type")
):
    class Type(Enum):
        CREATE = 0
        REMOVE = 1
        ALTER = 2
        REMOVE_ORDER_WRT = 3
        ALTER_FOO_TOGETHER = 4

    @cached_property
    def model_name_lower(self):
        return self.model_name.lower()

    @cached_property
    def field_name_lower(self):
        return self.field_name.lower()


class MigrationAutodetector:
    

    def __init__(self, from_state, to_state, questioner=None):
        self.from_state = from_state
        self.to_state = to_state
        self.questioner = questioner or MigrationQuestioner()
        self.existing_apps = {app for app, model in from_state.models}

    def changes(self, graph, trim_to_apps=None, convert_apps=None, migration_name=None):
        
        changes = self._detect_changes(convert_apps, graph)
        changes = self.arrange_for_graph(changes, graph, migration_name)
        if trim_to_apps:
            changes = self._trim_to_apps(changes, trim_to_apps)
        return changes

    def deep_deconstruct(self, obj):
        
        if isinstance(obj, list):
            return [self.deep_deconstruct(value) for value in obj]
        elif isinstance(obj, tuple):
            return tuple(self.deep_deconstruct(value) for value in obj)
        elif isinstance(obj, dict):
            return {key: self.deep_deconstruct(value) for key, value in obj.items()}
        elif isinstance(obj, functools.partial):
            return (
                obj.func,
                self.deep_deconstruct(obj.args),
                self.deep_deconstruct(obj.keywords),
            )
        elif isinstance(obj, COMPILED_REGEX_TYPE):
            return RegexObject(obj)
        elif isinstance(obj, type):
            
            
            return obj
        elif hasattr(obj, "deconstruct"):
            deconstructed = obj.deconstruct()
            if isinstance(obj, models.Field):
                
                deconstructed = deconstructed[1:]
            path, args, kwargs = deconstructed
            return (
                path,
                [self.deep_deconstruct(value) for value in args],
                {key: self.deep_deconstruct(value) for key, value in kwargs.items()},
            )
        else:
            return obj

    def only_relation_agnostic_fields(self, fields):
        
        fields_def = []
        for name, field in sorted(fields.items()):
            deconstruction = self.deep_deconstruct(field)
            if field.remote_field and field.remote_field.model:
                deconstruction[2].pop("to", None)
            fields_def.append(deconstruction)
        return fields_def

    def _detect_changes(self, convert_apps=None, graph=None):
        
        
        
        
        
        self.generated_operations = {}
        self.altered_indexes = {}
        self.altered_constraints = {}
        self.renamed_fields = {}

        
        
        self.old_model_keys = set()
        self.old_proxy_keys = set()
        self.old_unmanaged_keys = set()
        self.new_model_keys = set()
        self.new_proxy_keys = set()
        self.new_unmanaged_keys = set()
        for (app_label, model_name), model_state in self.from_state.models.items():
            if not model_state.options.get("managed", True):
                self.old_unmanaged_keys.add((app_label, model_name))
            elif app_label not in self.from_state.real_apps:
                if model_state.options.get("proxy"):
                    self.old_proxy_keys.add((app_label, model_name))
                else:
                    self.old_model_keys.add((app_label, model_name))

        for (app_label, model_name), model_state in self.to_state.models.items():
            if not model_state.options.get("managed", True):
                self.new_unmanaged_keys.add((app_label, model_name))
            elif app_label not in self.from_state.real_apps or (
                convert_apps and app_label in convert_apps
            ):
                if model_state.options.get("proxy"):
                    self.new_proxy_keys.add((app_label, model_name))
                else:
                    self.new_model_keys.add((app_label, model_name))

        self.from_state.resolve_fields_and_relations()
        self.to_state.resolve_fields_and_relations()

        
        self.generate_renamed_models()

        
        self._prepare_field_lists()
        self._generate_through_model_map()

        
        self.generate_deleted_models()
        self.generate_created_models()
        self.generate_deleted_proxies()
        self.generate_created_proxies()
        self.generate_altered_options()
        self.generate_altered_managers()
        self.generate_altered_db_table_comment()

        
        
        
        
        self.create_renamed_fields()
        
        
        
        self.create_altered_indexes()
        self.create_altered_constraints()
        
        self.generate_removed_constraints()
        self.generate_removed_indexes()
        
        self.generate_renamed_fields()
        self.generate_renamed_indexes()
        
        self.generate_removed_altered_unique_together()
        
        self.generate_removed_fields()
        self.generate_added_fields()
        self.generate_altered_fields()
        self.generate_altered_order_with_respect_to()
        self.generate_altered_unique_together()
        self.generate_added_indexes()
        self.generate_added_constraints()
        self.generate_altered_db_table()

        self._sort_migrations()
        self._build_migration_list(graph)
        self._optimize_migrations()

        return self.migrations

    def _prepare_field_lists(self):
        
        self.kept_model_keys = self.old_model_keys & self.new_model_keys
        self.kept_proxy_keys = self.old_proxy_keys & self.new_proxy_keys
        self.kept_unmanaged_keys = self.old_unmanaged_keys & self.new_unmanaged_keys
        self.through_users = {}
        self.old_field_keys = {
            (app_label, model_name, field_name)
            for app_label, model_name in self.kept_model_keys
            for field_name in self.from_state.models[
                app_label, self.renamed_models.get((app_label, model_name), model_name)
            ].fields
        }
        self.new_field_keys = {
            (app_label, model_name, field_name)
            for app_label, model_name in self.kept_model_keys
            for field_name in self.to_state.models[app_label, model_name].fields
        }

    def _generate_through_model_map(self):
        
        for app_label, model_name in sorted(self.old_model_keys):
            old_model_name = self.renamed_models.get(
                (app_label, model_name), model_name
            )
            old_model_state = self.from_state.models[app_label, old_model_name]
            for field_name, field in old_model_state.fields.items():
                if hasattr(field, "remote_field") and getattr(
                    field.remote_field, "through", None
                ):
                    through_key = resolve_relation(
                        field.remote_field.through, app_label, model_name
                    )
                    self.through_users[through_key] = (
                        app_label,
                        old_model_name,
                        field_name,
                    )

    @staticmethod
    def _resolve_dependency(dependency):
        
        if dependency.app_label != "__setting__":
            return dependency, False
        resolved_app_label, resolved_object_name = getattr(
            settings, dependency.model_name
        ).split(".")
        return (
            OperationDependency(
                resolved_app_label,
                resolved_object_name.lower(),
                dependency.field_name,
                dependency.type,
            ),
            True,
        )

    def _build_migration_list(self, graph=None):
        
        self.migrations = {}
        num_ops = sum(len(x) for x in self.generated_operations.values())
        chop_mode = False
        while num_ops:
            
            
            
            
            
            
            for app_label in sorted(self.generated_operations):
                chopped = []
                dependencies = set()
                for operation in list(self.generated_operations[app_label]):
                    deps_satisfied = True
                    operation_dependencies = set()
                    for dep in operation._auto_deps:
                        
                        
                        
                        
                        original_dep = dep
                        dep, is_swappable_dep = self._resolve_dependency(dep)
                        if dep.app_label != app_label:
                            
                            
                            for other_operation in self.generated_operations.get(
                                dep.app_label, []
                            ):
                                if self.check_dependency(other_operation, dep):
                                    deps_satisfied = False
                                    break
                            if not deps_satisfied:
                                break
                            else:
                                if is_swappable_dep:
                                    operation_dependencies.add(
                                        (
                                            original_dep.app_label,
                                            original_dep.model_name,
                                        )
                                    )
                                elif dep.app_label in self.migrations:
                                    operation_dependencies.add(
                                        (
                                            dep.app_label,
                                            self.migrations[dep.app_label][-1].name,
                                        )
                                    )
                                else:
                                    
                                    
                                    
                                    
                                    if chop_mode:
                                        
                                        
                                        
                                        
                                        
                                        
                                        if graph and graph.leaf_nodes(dep.app_label):
                                            operation_dependencies.add(
                                                graph.leaf_nodes(dep.app_label)[0]
                                            )
                                        else:
                                            operation_dependencies.add(
                                                (dep.app_label, "__first__")
                                            )
                                    else:
                                        deps_satisfied = False
                    if deps_satisfied:
                        chopped.append(operation)
                        dependencies.update(operation_dependencies)
                        del self.generated_operations[app_label][0]
                    else:
                        break
                
                if dependencies or chopped:
                    if not self.generated_operations[app_label] or chop_mode:
                        subclass = type(
                            "Migration",
                            (Migration,),
                            {"operations": [], "dependencies": []},
                        )
                        instance = subclass(
                            "auto_%i" % (len(self.migrations.get(app_label, [])) + 1),
                            app_label,
                        )
                        instance.dependencies = list(dependencies)
                        instance.operations = chopped
                        instance.initial = app_label not in self.existing_apps
                        self.migrations.setdefault(app_label, []).append(instance)
                        chop_mode = False
                    else:
                        self.generated_operations[app_label] = (
                            chopped + self.generated_operations[app_label]
                        )
            new_num_ops = sum(len(x) for x in self.generated_operations.values())
            if new_num_ops == num_ops:
                if not chop_mode:
                    chop_mode = True
                else:
                    raise ValueError(
                        "Cannot resolve operation dependencies: %r"
                        % self.generated_operations
                    )
            num_ops = new_num_ops

    def _sort_migrations(self):
        
        for app_label, ops in sorted(self.generated_operations.items()):
            ts = TopologicalSorter()
            for op in ops:
                ts.add(op)
                for dep in op._auto_deps:
                    
                    
                    dep = self._resolve_dependency(dep)[0]
                    if dep.app_label != app_label:
                        continue
                    ts.add(op, *(x for x in ops if self.check_dependency(x, dep)))
            self.generated_operations[app_label] = list(ts.static_order())

    def _optimize_migrations(self):
        
        for app_label, migrations in self.migrations.items():
            for m1, m2 in zip(migrations, migrations[1:]):
                m2.dependencies.append((app_label, m1.name))

        
        for migrations in self.migrations.values():
            for migration in migrations:
                migration.dependencies = list(set(migration.dependencies))

        
        for app_label, migrations in self.migrations.items():
            for migration in migrations:
                migration.operations = MigrationOptimizer().optimize(
                    migration.operations, app_label
                )

    def check_dependency(self, operation, dependency):
        
        
        if (
            dependency.field_name is None
            and dependency.type == OperationDependency.Type.CREATE
        ):
            return (
                isinstance(operation, operations.CreateModel)
                and operation.name_lower == dependency.model_name_lower
            )
        
        elif (
            dependency.field_name is not None
            and dependency.type == OperationDependency.Type.CREATE
        ):
            return (
                isinstance(operation, operations.CreateModel)
                and operation.name_lower == dependency.model_name_lower
                and any(dependency.field_name == x for x, y in operation.fields)
            ) or (
                isinstance(operation, operations.AddField)
                and operation.model_name_lower == dependency.model_name_lower
                and operation.name_lower == dependency.field_name_lower
            )
        
        elif (
            dependency.field_name is not None
            and dependency.type == OperationDependency.Type.REMOVE
        ):
            return (
                isinstance(operation, operations.RemoveField)
                and operation.model_name_lower == dependency.model_name_lower
                and operation.name_lower == dependency.field_name_lower
            )
        
        elif (
            dependency.field_name is None
            and dependency.type == OperationDependency.Type.REMOVE
        ):
            return (
                isinstance(operation, operations.DeleteModel)
                and operation.name_lower == dependency.model_name_lower
            )
        
        elif (
            dependency.field_name is not None
            and dependency.type == OperationDependency.Type.ALTER
        ):
            return (
                isinstance(operation, operations.AlterField)
                and operation.model_name_lower == dependency.model_name_lower
                and operation.name_lower == dependency.field_name_lower
            )
        
        elif (
            dependency.field_name is not None
            and dependency.type == OperationDependency.Type.REMOVE_ORDER_WRT
        ):
            return (
                isinstance(operation, operations.AlterOrderWithRespectTo)
                and operation.name_lower == dependency.model_name_lower
                and (operation.order_with_respect_to or "").lower()
                != dependency.field_name_lower
            )
        
        elif (
            dependency.field_name is not None
            and dependency.type == OperationDependency.Type.ALTER_FOO_TOGETHER
        ):
            return (
                isinstance(
                    operation,
                    (operations.AlterUniqueTogether, operations.AlterIndexTogether),
                )
                and operation.name_lower == dependency.model_name_lower
            )
        
        else:
            raise ValueError("Can't handle dependency %r" % (dependency,))

    def add_operation(self, app_label, operation, dependencies=None, beginning=False):
        
        
        operation._auto_deps = dependencies or []
        if beginning:
            self.generated_operations.setdefault(app_label, []).insert(0, operation)
        else:
            self.generated_operations.setdefault(app_label, []).append(operation)

    def swappable_first_key(self, item):
        
        try:
            model_state = self.to_state.models[item]
            base_names = {
                base if isinstance(base, str) else base.__name__
                for base in model_state.bases
            }
            string_version = "%s.%s" % (item[0], item[1])
            if (
                model_state.options.get("swappable")
                or "AbstractUser" in base_names
                or "AbstractBaseUser" in base_names
                or settings.AUTH_USER_MODEL.lower() == string_version.lower()
            ):
                return ("___" + item[0], "___" + item[1])
        except LookupError:
            pass
        return item

    def generate_renamed_models(self):
        
        self.renamed_models = {}
        self.renamed_models_rel = {}
        added_models = self.new_model_keys - self.old_model_keys
        for app_label, model_name in sorted(added_models):
            model_state = self.to_state.models[app_label, model_name]
            model_fields_def = self.only_relation_agnostic_fields(model_state.fields)

            removed_models = self.old_model_keys - self.new_model_keys
            for rem_app_label, rem_model_name in removed_models:
                if rem_app_label == app_label:
                    rem_model_state = self.from_state.models[
                        rem_app_label, rem_model_name
                    ]
                    rem_model_fields_def = self.only_relation_agnostic_fields(
                        rem_model_state.fields
                    )
                    if model_fields_def == rem_model_fields_def:
                        if self.questioner.ask_rename_model(
                            rem_model_state, model_state
                        ):
                            dependencies = []
                            fields = list(model_state.fields.values()) + [
                                field.remote_field
                                for relations in self.to_state.relations[
                                    app_label, model_name
                                ].values()
                                for field in relations.values()
                            ]
                            for field in fields:
                                if field.is_relation:
                                    dependencies.extend(
                                        self._get_dependencies_for_foreign_key(
                                            app_label,
                                            model_name,
                                            field,
                                            self.to_state,
                                        )
                                    )
                            self.add_operation(
                                app_label,
                                operations.RenameModel(
                                    old_name=rem_model_state.name,
                                    new_name=model_state.name,
                                ),
                                dependencies=dependencies,
                            )
                            self.renamed_models[app_label, model_name] = rem_model_name
                            renamed_models_rel_key = "%s.%s" % (
                                rem_model_state.app_label,
                                rem_model_state.name_lower,
                            )
                            self.renamed_models_rel[
                                renamed_models_rel_key
                            ] = "%s.%s" % (
                                model_state.app_label,
                                model_state.name_lower,
                            )
                            self.old_model_keys.remove((rem_app_label, rem_model_name))
                            self.old_model_keys.add((app_label, model_name))
                            break

    def generate_created_models(self):
        
        old_keys = self.old_model_keys | self.old_unmanaged_keys
        added_models = self.new_model_keys - old_keys
        added_unmanaged_models = self.new_unmanaged_keys - old_keys
        all_added_models = chain(
            sorted(added_models, key=self.swappable_first_key, reverse=True),
            sorted(added_unmanaged_models, key=self.swappable_first_key, reverse=True),
        )
        for app_label, model_name in all_added_models:
            model_state = self.to_state.models[app_label, model_name]
            
            related_fields = {}
            primary_key_rel = None
            for field_name, field in model_state.fields.items():
                if field.remote_field:
                    if field.remote_field.model:
                        if field.primary_key:
                            primary_key_rel = field.remote_field.model
                        elif not field.remote_field.parent_link:
                            related_fields[field_name] = field
                    if getattr(field.remote_field, "through", None):
                        related_fields[field_name] = field

            
            indexes = model_state.options.pop("indexes")
            constraints = model_state.options.pop("constraints")
            unique_together = model_state.options.pop("unique_together", None)
            order_with_respect_to = model_state.options.pop(
                "order_with_respect_to", None
            )
            
            dependencies = [
                OperationDependency(
                    app_label, model_name, None, OperationDependency.Type.REMOVE
                ),
            ]
            
            for base in model_state.bases:
                if isinstance(base, str) and "." in base:
                    base_app_label, base_name = base.split(".", 1)
                    dependencies.append(
                        OperationDependency(
                            base_app_label,
                            base_name,
                            None,
                            OperationDependency.Type.CREATE,
                        )
                    )
                    
                    
                    old_base_model_state = self.from_state.models.get(
                        (base_app_label, base_name)
                    )
                    new_base_model_state = self.to_state.models.get(
                        (base_app_label, base_name)
                    )
                    if old_base_model_state and new_base_model_state:
                        removed_base_fields = (
                            set(old_base_model_state.fields)
                            .difference(
                                new_base_model_state.fields,
                            )
                            .intersection(model_state.fields)
                        )
                        for removed_base_field in removed_base_fields:
                            dependencies.append(
                                OperationDependency(
                                    base_app_label,
                                    base_name,
                                    removed_base_field,
                                    OperationDependency.Type.REMOVE,
                                )
                            )
            
            if primary_key_rel:
                dependencies.append(
                    OperationDependency(
                        *resolve_relation(primary_key_rel, app_label, model_name),
                        None,
                        OperationDependency.Type.CREATE,
                    ),
                )
            
            self.add_operation(
                app_label,
                operations.CreateModel(
                    name=model_state.name,
                    fields=[
                        d
                        for d in model_state.fields.items()
                        if d[0] not in related_fields
                    ],
                    options=model_state.options,
                    bases=model_state.bases,
                    managers=model_state.managers,
                ),
                dependencies=dependencies,
                beginning=True,
            )

            
            if not model_state.options.get("managed", True):
                continue

            
            for name, field in sorted(related_fields.items()):
                dependencies = self._get_dependencies_for_foreign_key(
                    app_label,
                    model_name,
                    field,
                    self.to_state,
                )
                
                dependencies.append(
                    OperationDependency(
                        app_label, model_name, None, OperationDependency.Type.CREATE
                    )
                )
                
                self.add_operation(
                    app_label,
                    operations.AddField(
                        model_name=model_name,
                        name=name,
                        field=field,
                    ),
                    dependencies=list(set(dependencies)),
                )
            
            if order_with_respect_to:
                self.add_operation(
                    app_label,
                    operations.AlterOrderWithRespectTo(
                        name=model_name,
                        order_with_respect_to=order_with_respect_to,
                    ),
                    dependencies=[
                        OperationDependency(
                            app_label,
                            model_name,
                            order_with_respect_to,
                            OperationDependency.Type.CREATE,
                        ),
                        OperationDependency(
                            app_label, model_name, None, OperationDependency.Type.CREATE
                        ),
                    ],
                )
            related_dependencies = [
                OperationDependency(
                    app_label, model_name, name, OperationDependency.Type.CREATE
                )
                for name in sorted(related_fields)
            ]
            related_dependencies.append(
                OperationDependency(
                    app_label, model_name, None, OperationDependency.Type.CREATE
                )
            )
            for index in indexes:
                self.add_operation(
                    app_label,
                    operations.AddIndex(
                        model_name=model_name,
                        index=index,
                    ),
                    dependencies=related_dependencies,
                )
            for constraint in constraints:
                self.add_operation(
                    app_label,
                    operations.AddConstraint(
                        model_name=model_name,
                        constraint=constraint,
                    ),
                    dependencies=related_dependencies,
                )
            if unique_together:
                self.add_operation(
                    app_label,
                    operations.AlterUniqueTogether(
                        name=model_name,
                        unique_together=unique_together,
                    ),
                    dependencies=related_dependencies,
                )
            
            
            relations = self.to_state.relations
            if (app_label, model_name) in self.old_proxy_keys:
                for related_model_key, related_fields in relations[
                    app_label, model_name
                ].items():
                    related_model_state = self.to_state.models[related_model_key]
                    for related_field_name, related_field in related_fields.items():
                        self.add_operation(
                            related_model_state.app_label,
                            operations.AlterField(
                                model_name=related_model_state.name,
                                name=related_field_name,
                                field=related_field,
                            ),
                            dependencies=[
                                OperationDependency(
                                    app_label,
                                    model_name,
                                    None,
                                    OperationDependency.Type.CREATE,
                                )
                            ],
                        )

    def generate_created_proxies(self):
        
        added = self.new_proxy_keys - self.old_proxy_keys
        for app_label, model_name in sorted(added):
            model_state = self.to_state.models[app_label, model_name]
            assert model_state.options.get("proxy")
            
            dependencies = [
                OperationDependency(
                    app_label, model_name, None, OperationDependency.Type.REMOVE
                ),
            ]
            
            for base in model_state.bases:
                if isinstance(base, str) and "." in base:
                    base_app_label, base_name = base.split(".", 1)
                    dependencies.append(
                        OperationDependency(
                            base_app_label,
                            base_name,
                            None,
                            OperationDependency.Type.CREATE,
                        )
                    )
            
            self.add_operation(
                app_label,
                operations.CreateModel(
                    name=model_state.name,
                    fields=[],
                    options=model_state.options,
                    bases=model_state.bases,
                    managers=model_state.managers,
                ),
                
                dependencies=dependencies,
            )

    def generate_deleted_models(self):
        
        new_keys = self.new_model_keys | self.new_unmanaged_keys
        deleted_models = self.old_model_keys - new_keys
        deleted_unmanaged_models = self.old_unmanaged_keys - new_keys
        all_deleted_models = chain(
            sorted(deleted_models), sorted(deleted_unmanaged_models)
        )
        for app_label, model_name in all_deleted_models:
            model_state = self.from_state.models[app_label, model_name]
            
            related_fields = {}
            for field_name, field in model_state.fields.items():
                if field.remote_field:
                    if field.remote_field.model:
                        related_fields[field_name] = field
                    if getattr(field.remote_field, "through", None):
                        related_fields[field_name] = field
            
            unique_together = model_state.options.pop("unique_together", None)
            if unique_together:
                self.add_operation(
                    app_label,
                    operations.AlterUniqueTogether(
                        name=model_name,
                        unique_together=None,
                    ),
                )
            
            for name in sorted(related_fields):
                self.add_operation(
                    app_label,
                    operations.RemoveField(
                        model_name=model_name,
                        name=name,
                    ),
                )
            
            
            
            
            dependencies = []
            relations = self.from_state.relations
            for (
                related_object_app_label,
                object_name,
            ), relation_related_fields in relations[app_label, model_name].items():
                for field_name, field in relation_related_fields.items():
                    dependencies.append(
                        OperationDependency(
                            related_object_app_label,
                            object_name,
                            field_name,
                            OperationDependency.Type.REMOVE,
                        ),
                    )
                    if not field.many_to_many:
                        dependencies.append(
                            OperationDependency(
                                related_object_app_label,
                                object_name,
                                field_name,
                                OperationDependency.Type.ALTER,
                            ),
                        )

            for name in sorted(related_fields):
                dependencies.append(
                    OperationDependency(
                        app_label, model_name, name, OperationDependency.Type.REMOVE
                    )
                )
            
            through_user = self.through_users.get((app_label, model_state.name_lower))
            if through_user:
                dependencies.append(
                    OperationDependency(*through_user, OperationDependency.Type.REMOVE),
                )
            
            self.add_operation(
                app_label,
                operations.DeleteModel(
                    name=model_state.name,
                ),
                dependencies=list(set(dependencies)),
            )

    def generate_deleted_proxies(self):
        
        deleted = self.old_proxy_keys - self.new_proxy_keys
        for app_label, model_name in sorted(deleted):
            model_state = self.from_state.models[app_label, model_name]
            assert model_state.options.get("proxy")
            self.add_operation(
                app_label,
                operations.DeleteModel(
                    name=model_state.name,
                ),
            )

    def create_renamed_fields(self):
        
        self.renamed_operations = []
        old_field_keys = self.old_field_keys.copy()
        for app_label, model_name, field_name in sorted(
            self.new_field_keys - old_field_keys
        ):
            old_model_name = self.renamed_models.get(
                (app_label, model_name), model_name
            )
            old_model_state = self.from_state.models[app_label, old_model_name]
            new_model_state = self.to_state.models[app_label, model_name]
            field = new_model_state.get_field(field_name)
            
            field_dec = self.deep_deconstruct(field)
            for rem_app_label, rem_model_name, rem_field_name in sorted(
                old_field_keys - self.new_field_keys
            ):
                if rem_app_label == app_label and rem_model_name == model_name:
                    old_field = old_model_state.get_field(rem_field_name)
                    old_field_dec = self.deep_deconstruct(old_field)
                    if (
                        field.remote_field
                        and field.remote_field.model
                        and "to" in old_field_dec[2]
                    ):
                        old_rel_to = old_field_dec[2]["to"]
                        if old_rel_to in self.renamed_models_rel:
                            old_field_dec[2]["to"] = self.renamed_models_rel[old_rel_to]
                    old_field.set_attributes_from_name(rem_field_name)
                    old_db_column = old_field.get_attname_column()[1]
                    if old_field_dec == field_dec or (
                        
                        
                        old_field_dec[0:2] == field_dec[0:2]
                        and dict(old_field_dec[2], db_column=old_db_column)
                        == field_dec[2]
                    ):
                        if self.questioner.ask_rename(
                            model_name, rem_field_name, field_name, field
                        ):
                            self.renamed_operations.append(
                                (
                                    rem_app_label,
                                    rem_model_name,
                                    old_field.db_column,
                                    rem_field_name,
                                    app_label,
                                    model_name,
                                    field,
                                    field_name,
                                )
                            )
                            old_field_keys.remove(
                                (rem_app_label, rem_model_name, rem_field_name)
                            )
                            old_field_keys.add((app_label, model_name, field_name))
                            self.renamed_fields[
                                app_label, model_name, field_name
                            ] = rem_field_name
                            break

    def generate_renamed_fields(self):
        
        for (
            rem_app_label,
            rem_model_name,
            rem_db_column,
            rem_field_name,
            app_label,
            model_name,
            field,
            field_name,
        ) in self.renamed_operations:
            
            
            
            if rem_db_column != field.db_column:
                altered_field = field.clone()
                altered_field.name = rem_field_name
                self.add_operation(
                    app_label,
                    operations.AlterField(
                        model_name=model_name,
                        name=rem_field_name,
                        field=altered_field,
                    ),
                )
            self.add_operation(
                app_label,
                operations.RenameField(
                    model_name=model_name,
                    old_name=rem_field_name,
                    new_name=field_name,
                ),
            )
            self.old_field_keys.remove((rem_app_label, rem_model_name, rem_field_name))
            self.old_field_keys.add((app_label, model_name, field_name))

    def generate_added_fields(self):
        
        for app_label, model_name, field_name in sorted(
            self.new_field_keys - self.old_field_keys
        ):
            self._generate_added_field(app_label, model_name, field_name)

    def _generate_added_field(self, app_label, model_name, field_name):
        field = self.to_state.models[app_label, model_name].get_field(field_name)
        
        dependencies = [
            OperationDependency(
                app_label, model_name, field_name, OperationDependency.Type.REMOVE
            )
        ]
        
        if field.remote_field and field.remote_field.model:
            dependencies.extend(
                self._get_dependencies_for_foreign_key(
                    app_label,
                    model_name,
                    field,
                    self.to_state,
                )
            )
        
        
        time_fields = (models.DateField, models.DateTimeField, models.TimeField)
        preserve_default = (
            field.null
            or field.has_default()
            or field.db_default is not models.NOT_PROVIDED
            or field.many_to_many
            or (field.blank and field.empty_strings_allowed)
            or (isinstance(field, time_fields) and field.auto_now)
        )
        if not preserve_default:
            field = field.clone()
            if isinstance(field, time_fields) and field.auto_now_add:
                field.default = self.questioner.ask_auto_now_add_addition(
                    field_name, model_name
                )
            else:
                field.default = self.questioner.ask_not_null_addition(
                    field_name, model_name
                )
        if (
            field.unique
            and field.default is not models.NOT_PROVIDED
            and callable(field.default)
        ):
            self.questioner.ask_unique_callable_default_addition(field_name, model_name)
        self.add_operation(
            app_label,
            operations.AddField(
                model_name=model_name,
                name=field_name,
                field=field,
                preserve_default=preserve_default,
            ),
            dependencies=dependencies,
        )

    def generate_removed_fields(self):
        
        for app_label, model_name, field_name in sorted(
            self.old_field_keys - self.new_field_keys
        ):
            self._generate_removed_field(app_label, model_name, field_name)

    def _generate_removed_field(self, app_label, model_name, field_name):
        self.add_operation(
            app_label,
            operations.RemoveField(
                model_name=model_name,
                name=field_name,
            ),
            
            
            
            dependencies=[
                OperationDependency(
                    app_label,
                    model_name,
                    field_name,
                    OperationDependency.Type.REMOVE_ORDER_WRT,
                ),
                OperationDependency(
                    app_label,
                    model_name,
                    field_name,
                    OperationDependency.Type.ALTER_FOO_TOGETHER,
                ),
            ],
        )

    def generate_altered_fields(self):
        
        for app_label, model_name, field_name in sorted(
            self.old_field_keys & self.new_field_keys
        ):
            
            old_model_name = self.renamed_models.get(
                (app_label, model_name), model_name
            )
            old_field_name = self.renamed_fields.get(
                (app_label, model_name, field_name), field_name
            )
            old_field = self.from_state.models[app_label, old_model_name].get_field(
                old_field_name
            )
            new_field = self.to_state.models[app_label, model_name].get_field(
                field_name
            )
            dependencies = []
            
            
            if hasattr(new_field, "remote_field") and getattr(
                new_field.remote_field, "model", None
            ):
                rename_key = resolve_relation(
                    new_field.remote_field.model, app_label, model_name
                )
                if rename_key in self.renamed_models:
                    new_field.remote_field.model = old_field.remote_field.model
                
                remote_field_name = getattr(new_field.remote_field, "field_name", None)
                if remote_field_name:
                    to_field_rename_key = rename_key + (remote_field_name,)
                    if to_field_rename_key in self.renamed_fields:
                        
                        
                        
                        new_field.remote_field.model = old_field.remote_field.model
                        new_field.remote_field.field_name = (
                            old_field.remote_field.field_name
                        )
                
                from_fields = getattr(new_field, "from_fields", None)
                if from_fields:
                    from_rename_key = (app_label, model_name)
                    new_field.from_fields = tuple(
                        [
                            self.renamed_fields.get(
                                from_rename_key + (from_field,), from_field
                            )
                            for from_field in from_fields
                        ]
                    )
                    new_field.to_fields = tuple(
                        [
                            self.renamed_fields.get(rename_key + (to_field,), to_field)
                            for to_field in new_field.to_fields
                        ]
                    )
                    if old_from_fields := getattr(old_field, "from_fields", None):
                        old_field.from_fields = tuple(old_from_fields)
                        old_field.to_fields = tuple(old_field.to_fields)
                dependencies.extend(
                    self._get_dependencies_for_foreign_key(
                        app_label,
                        model_name,
                        new_field,
                        self.to_state,
                    )
                )
            if hasattr(new_field, "remote_field") and getattr(
                new_field.remote_field, "through", None
            ):
                rename_key = resolve_relation(
                    new_field.remote_field.through, app_label, model_name
                )
                if rename_key in self.renamed_models:
                    new_field.remote_field.through = old_field.remote_field.through
            old_field_dec = self.deep_deconstruct(old_field)
            new_field_dec = self.deep_deconstruct(new_field)
            
            
            
            if old_field_dec != new_field_dec and old_field_name == field_name:
                both_m2m = old_field.many_to_many and new_field.many_to_many
                neither_m2m = not old_field.many_to_many and not new_field.many_to_many
                if both_m2m or neither_m2m:
                    
                    preserve_default = True
                    if (
                        old_field.null
                        and not new_field.null
                        and not new_field.has_default()
                        and new_field.db_default is models.NOT_PROVIDED
                        and not new_field.many_to_many
                    ):
                        field = new_field.clone()
                        new_default = self.questioner.ask_not_null_alteration(
                            field_name, model_name
                        )
                        if new_default is not models.NOT_PROVIDED:
                            field.default = new_default
                            preserve_default = False
                    else:
                        field = new_field
                    self.add_operation(
                        app_label,
                        operations.AlterField(
                            model_name=model_name,
                            name=field_name,
                            field=field,
                            preserve_default=preserve_default,
                        ),
                        dependencies=dependencies,
                    )
                else:
                    
                    self._generate_removed_field(app_label, model_name, field_name)
                    self._generate_added_field(app_label, model_name, field_name)

    def create_altered_indexes(self):
        option_name = operations.AddIndex.option_name
        self.renamed_index_together_values = defaultdict(list)

        for app_label, model_name in sorted(self.kept_model_keys):
            old_model_name = self.renamed_models.get(
                (app_label, model_name), model_name
            )
            old_model_state = self.from_state.models[app_label, old_model_name]
            new_model_state = self.to_state.models[app_label, model_name]

            old_indexes = old_model_state.options[option_name]
            new_indexes = new_model_state.options[option_name]
            added_indexes = [idx for idx in new_indexes if idx not in old_indexes]
            removed_indexes = [idx for idx in old_indexes if idx not in new_indexes]
            renamed_indexes = []
            
            remove_from_added = []
            remove_from_removed = []
            for new_index in added_indexes:
                new_index_dec = new_index.deconstruct()
                new_index_name = new_index_dec[2].pop("name")
                for old_index in removed_indexes:
                    old_index_dec = old_index.deconstruct()
                    old_index_name = old_index_dec[2].pop("name")
                    
                    if (
                        new_index_dec == old_index_dec
                        and new_index_name != old_index_name
                    ):
                        renamed_indexes.append((old_index_name, new_index_name, None))
                        remove_from_added.append(new_index)
                        remove_from_removed.append(old_index)
            
            for (
                old_value,
                new_value,
                index_together_app_label,
                index_together_model_name,
                dependencies,
            ) in self._get_altered_foo_together_operations(
                operations.AlterIndexTogether.option_name
            ):
                if (
                    app_label != index_together_app_label
                    or model_name != index_together_model_name
                ):
                    continue
                removed_values = old_value.difference(new_value)
                for removed_index_together in removed_values:
                    renamed_index_together_indexes = []
                    for new_index in added_indexes:
                        _, args, kwargs = new_index.deconstruct()
                        
                        if (
                            not args
                            and new_index.fields == list(removed_index_together)
                            and set(kwargs) == {"name", "fields"}
                        ):
                            renamed_index_together_indexes.append(new_index)

                    if len(renamed_index_together_indexes) == 1:
                        renamed_index = renamed_index_together_indexes[0]
                        remove_from_added.append(renamed_index)
                        renamed_indexes.append(
                            (None, renamed_index.name, removed_index_together)
                        )
                        self.renamed_index_together_values[
                            index_together_app_label, index_together_model_name
                        ].append(removed_index_together)
            
            
            added_indexes = [
                idx for idx in added_indexes if idx not in remove_from_added
            ]
            removed_indexes = [
                idx for idx in removed_indexes if idx not in remove_from_removed
            ]

            self.altered_indexes.update(
                {
                    (app_label, model_name): {
                        "added_indexes": added_indexes,
                        "removed_indexes": removed_indexes,
                        "renamed_indexes": renamed_indexes,
                    }
                }
            )

    def generate_added_indexes(self):
        for (app_label, model_name), alt_indexes in self.altered_indexes.items():
            dependencies = self._get_dependencies_for_model(app_label, model_name)
            for index in alt_indexes["added_indexes"]:
                self.add_operation(
                    app_label,
                    operations.AddIndex(
                        model_name=model_name,
                        index=index,
                    ),
                    dependencies=dependencies,
                )

    def generate_removed_indexes(self):
        for (app_label, model_name), alt_indexes in self.altered_indexes.items():
            for index in alt_indexes["removed_indexes"]:
                self.add_operation(
                    app_label,
                    operations.RemoveIndex(
                        model_name=model_name,
                        name=index.name,
                    ),
                )

    def generate_renamed_indexes(self):
        for (app_label, model_name), alt_indexes in self.altered_indexes.items():
            for old_index_name, new_index_name, old_fields in alt_indexes[
                "renamed_indexes"
            ]:
                self.add_operation(
                    app_label,
                    operations.RenameIndex(
                        model_name=model_name,
                        new_name=new_index_name,
                        old_name=old_index_name,
                        old_fields=old_fields,
                    ),
                )

    def create_altered_constraints(self):
        option_name = operations.AddConstraint.option_name
        for app_label, model_name in sorted(self.kept_model_keys):
            old_model_name = self.renamed_models.get(
                (app_label, model_name), model_name
            )
            old_model_state = self.from_state.models[app_label, old_model_name]
            new_model_state = self.to_state.models[app_label, model_name]

            old_constraints = old_model_state.options[option_name]
            new_constraints = new_model_state.options[option_name]
            add_constraints = [c for c in new_constraints if c not in old_constraints]
            rem_constraints = [c for c in old_constraints if c not in new_constraints]

            self.altered_constraints.update(
                {
                    (app_label, model_name): {
                        "added_constraints": add_constraints,
                        "removed_constraints": rem_constraints,
                    }
                }
            )

    def generate_added_constraints(self):
        for (
            app_label,
            model_name,
        ), alt_constraints in self.altered_constraints.items():
            dependencies = self._get_dependencies_for_model(app_label, model_name)
            for constraint in alt_constraints["added_constraints"]:
                self.add_operation(
                    app_label,
                    operations.AddConstraint(
                        model_name=model_name,
                        constraint=constraint,
                    ),
                    dependencies=dependencies,
                )

    def generate_removed_constraints(self):
        for (
            app_label,
            model_name,
        ), alt_constraints in self.altered_constraints.items():
            for constraint in alt_constraints["removed_constraints"]:
                self.add_operation(
                    app_label,
                    operations.RemoveConstraint(
                        model_name=model_name,
                        name=constraint.name,
                    ),
                )

    @staticmethod
    def _get_dependencies_for_foreign_key(app_label, model_name, field, project_state):
        remote_field_model = None
        if hasattr(field.remote_field, "model"):
            remote_field_model = field.remote_field.model
        else:
            relations = project_state.relations[app_label, model_name]
            for (remote_app_label, remote_model_name), fields in relations.items():
                if any(
                    field == related_field.remote_field
                    for related_field in fields.values()
                ):
                    remote_field_model = f"{remote_app_label}.{remote_model_name}"
                    break
        
        swappable_setting = getattr(field, "swappable_setting", None)
        if swappable_setting is not None:
            dep_app_label = "__setting__"
            dep_object_name = swappable_setting
        else:
            dep_app_label, dep_object_name = resolve_relation(
                remote_field_model,
                app_label,
                model_name,
            )
        dependencies = [
            OperationDependency(
                dep_app_label, dep_object_name, None, OperationDependency.Type.CREATE
            )
        ]
        if getattr(field.remote_field, "through", None):
            through_app_label, through_object_name = resolve_relation(
                field.remote_field.through,
                app_label,
                model_name,
            )
            dependencies.append(
                OperationDependency(
                    through_app_label,
                    through_object_name,
                    None,
                    OperationDependency.Type.CREATE,
                )
            )
        return dependencies

    def _get_dependencies_for_model(self, app_label, model_name):
        
        dependencies = []
        model_state = self.to_state.models[app_label, model_name]
        for field in model_state.fields.values():
            if field.is_relation:
                dependencies.extend(
                    self._get_dependencies_for_foreign_key(
                        app_label,
                        model_name,
                        field,
                        self.to_state,
                    )
                )
        return dependencies

    def _get_altered_foo_together_operations(self, option_name):
        for app_label, model_name in sorted(self.kept_model_keys):
            old_model_name = self.renamed_models.get(
                (app_label, model_name), model_name
            )
            old_model_state = self.from_state.models[app_label, old_model_name]
            new_model_state = self.to_state.models[app_label, model_name]

            
            old_value = old_model_state.options.get(option_name)
            old_value = (
                {
                    tuple(
                        self.renamed_fields.get((app_label, model_name, n), n)
                        for n in unique
                    )
                    for unique in old_value
                }
                if old_value
                else set()
            )

            new_value = new_model_state.options.get(option_name)
            new_value = set(new_value) if new_value else set()

            if old_value != new_value:
                dependencies = []
                for foo_togethers in new_value:
                    for field_name in foo_togethers:
                        field = new_model_state.get_field(field_name)
                        if field.remote_field and field.remote_field.model:
                            dependencies.extend(
                                self._get_dependencies_for_foreign_key(
                                    app_label,
                                    model_name,
                                    field,
                                    self.to_state,
                                )
                            )
                yield (
                    old_value,
                    new_value,
                    app_label,
                    model_name,
                    dependencies,
                )

    def _generate_removed_altered_foo_together(self, operation):
        for (
            old_value,
            new_value,
            app_label,
            model_name,
            dependencies,
        ) in self._get_altered_foo_together_operations(operation.option_name):
            if operation == operations.AlterIndexTogether:
                old_value = {
                    value
                    for value in old_value
                    if value
                    not in self.renamed_index_together_values[app_label, model_name]
                }
            removal_value = new_value.intersection(old_value)
            if removal_value or old_value:
                self.add_operation(
                    app_label,
                    operation(
                        name=model_name, **{operation.option_name: removal_value}
                    ),
                    dependencies=dependencies,
                )

    def generate_removed_altered_unique_together(self):
        self._generate_removed_altered_foo_together(operations.AlterUniqueTogether)

    def _generate_altered_foo_together(self, operation):
        for (
            old_value,
            new_value,
            app_label,
            model_name,
            dependencies,
        ) in self._get_altered_foo_together_operations(operation.option_name):
            removal_value = new_value.intersection(old_value)
            if new_value != removal_value:
                self.add_operation(
                    app_label,
                    operation(name=model_name, **{operation.option_name: new_value}),
                    dependencies=dependencies,
                )

    def generate_altered_unique_together(self):
        self._generate_altered_foo_together(operations.AlterUniqueTogether)

    def generate_altered_db_table(self):
        models_to_check = self.kept_model_keys.union(
            self.kept_proxy_keys, self.kept_unmanaged_keys
        )
        for app_label, model_name in sorted(models_to_check):
            old_model_name = self.renamed_models.get(
                (app_label, model_name), model_name
            )
            old_model_state = self.from_state.models[app_label, old_model_name]
            new_model_state = self.to_state.models[app_label, model_name]
            old_db_table_name = old_model_state.options.get("db_table")
            new_db_table_name = new_model_state.options.get("db_table")
            if old_db_table_name != new_db_table_name:
                self.add_operation(
                    app_label,
                    operations.AlterModelTable(
                        name=model_name,
                        table=new_db_table_name,
                    ),
                )

    def generate_altered_db_table_comment(self):
        models_to_check = self.kept_model_keys.union(
            self.kept_proxy_keys, self.kept_unmanaged_keys
        )
        for app_label, model_name in sorted(models_to_check):
            old_model_name = self.renamed_models.get(
                (app_label, model_name), model_name
            )
            old_model_state = self.from_state.models[app_label, old_model_name]
            new_model_state = self.to_state.models[app_label, model_name]

            old_db_table_comment = old_model_state.options.get("db_table_comment")
            new_db_table_comment = new_model_state.options.get("db_table_comment")
            if old_db_table_comment != new_db_table_comment:
                self.add_operation(
                    app_label,
                    operations.AlterModelTableComment(
                        name=model_name,
                        table_comment=new_db_table_comment,
                    ),
                )

    def generate_altered_options(self):
        
        models_to_check = self.kept_model_keys.union(
            self.kept_proxy_keys,
            self.kept_unmanaged_keys,
            
            self.old_unmanaged_keys & self.new_model_keys,
            
            self.old_model_keys & self.new_unmanaged_keys,
        )

        for app_label, model_name in sorted(models_to_check):
            old_model_name = self.renamed_models.get(
                (app_label, model_name), model_name
            )
            old_model_state = self.from_state.models[app_label, old_model_name]
            new_model_state = self.to_state.models[app_label, model_name]
            old_options = {
                key: value
                for key, value in old_model_state.options.items()
                if key in AlterModelOptions.ALTER_OPTION_KEYS
            }
            new_options = {
                key: value
                for key, value in new_model_state.options.items()
                if key in AlterModelOptions.ALTER_OPTION_KEYS
            }
            if old_options != new_options:
                self.add_operation(
                    app_label,
                    operations.AlterModelOptions(
                        name=model_name,
                        options=new_options,
                    ),
                )

    def generate_altered_order_with_respect_to(self):
        for app_label, model_name in sorted(self.kept_model_keys):
            old_model_name = self.renamed_models.get(
                (app_label, model_name), model_name
            )
            old_model_state = self.from_state.models[app_label, old_model_name]
            new_model_state = self.to_state.models[app_label, model_name]
            if old_model_state.options.get(
                "order_with_respect_to"
            ) != new_model_state.options.get("order_with_respect_to"):
                
                
                dependencies = []
                if new_model_state.options.get("order_with_respect_to"):
                    dependencies.append(
                        OperationDependency(
                            app_label,
                            model_name,
                            new_model_state.options["order_with_respect_to"],
                            OperationDependency.Type.CREATE,
                        )
                    )
                
                self.add_operation(
                    app_label,
                    operations.AlterOrderWithRespectTo(
                        name=model_name,
                        order_with_respect_to=new_model_state.options.get(
                            "order_with_respect_to"
                        ),
                    ),
                    dependencies=dependencies,
                )

    def generate_altered_managers(self):
        for app_label, model_name in sorted(self.kept_model_keys):
            old_model_name = self.renamed_models.get(
                (app_label, model_name), model_name
            )
            old_model_state = self.from_state.models[app_label, old_model_name]
            new_model_state = self.to_state.models[app_label, model_name]
            if old_model_state.managers != new_model_state.managers:
                self.add_operation(
                    app_label,
                    operations.AlterModelManagers(
                        name=model_name,
                        managers=new_model_state.managers,
                    ),
                )

    def arrange_for_graph(self, changes, graph, migration_name=None):
        
        leaves = graph.leaf_nodes()
        name_map = {}
        for app_label, migrations in list(changes.items()):
            if not migrations:
                continue
            
            app_leaf = None
            for leaf in leaves:
                if leaf[0] == app_label:
                    app_leaf = leaf
                    break
            
            if app_leaf is None and not self.questioner.ask_initial(app_label):
                
                for migration in migrations:
                    name_map[(app_label, migration.name)] = (app_label, "__first__")
                del changes[app_label]
                continue
            
            if app_leaf is None:
                next_number = 1
            else:
                next_number = (self.parse_number(app_leaf[1]) or 0) + 1
            
            for i, migration in enumerate(migrations):
                if i == 0 and app_leaf:
                    migration.dependencies.append(app_leaf)
                new_name_parts = ["%04i" % next_number]
                if migration_name:
                    new_name_parts.append(migration_name)
                elif i == 0 and not app_leaf:
                    new_name_parts.append("initial")
                else:
                    new_name_parts.append(migration.suggest_name()[:100])
                new_name = "_".join(new_name_parts)
                name_map[(app_label, migration.name)] = (app_label, new_name)
                next_number += 1
                migration.name = new_name
        
        for migrations in changes.values():
            for migration in migrations:
                migration.dependencies = [
                    name_map.get(d, d) for d in migration.dependencies
                ]
        return changes

    def _trim_to_apps(self, changes, app_labels):
        
        
        app_dependencies = {}
        for app_label, migrations in changes.items():
            for migration in migrations:
                for dep_app_label, name in migration.dependencies:
                    app_dependencies.setdefault(app_label, set()).add(dep_app_label)
        required_apps = set(app_labels)
        
        old_required_apps = None
        while old_required_apps != required_apps:
            old_required_apps = set(required_apps)
            required_apps.update(
                *[app_dependencies.get(app_label, ()) for app_label in required_apps]
            )
        
        for app_label in list(changes):
            if app_label not in required_apps:
                del changes[app_label]
        return changes

    @classmethod
    def parse_number(cls, name):
        
        if squashed_match := re.search(r".*_squashed_(\d+)", name):
            return int(squashed_match[1])
        match = re.match(r"^\d+", name)
        if match:
            return int(match[0])
        return None