o 3aE@s^ddlmZddlmZmZddlmZddlm Z ddl m Z ddl m Z Gdd d Zd S) )apps) migrationsrouter)InvalidMigrationPlan)MigrationLoader)MigrationRecorder) ProjectStatec@steZdZdZdddZdddZddd Zdd d Zd d ZddZ dddZ ddZ dddZ ddZ ddZdS)MigrationExecutorzu End-to-end migration execution - load migrations and run them up or down to a specified set of targets. NcCs(||_t|j|_t|j|_||_dSN) connectionrloaderrrecorderprogress_callback)selfr rr?/usr/lib/python3/dist-packages/django/db/migrations/executor.py__init__s   zMigrationExecutor.__init__Fc sPg}|ri}nt|jj}|D]ddurH|jjD])}|ddkrF|jj|D]}||vrE||jjj|df||q.qq|vrt fdd|jjj j D}|D]!}|jj|D]}||vr||jjj|df||qhq_q|jj D]}||vr||jjj|df|jjj|||<qq|S)z\ Given a set of targets, return a list of (Migration instance, backwards?). rNrTc3s$|] }|ddkr|VqdS)rNr).0ntargetrr /sz3MigrationExecutor.migration_plan..F) dictr applied_migrationsgraph root_nodesbackwards_planappendnodespopsortednode_mapchildren forwards_plan) rtargets clean_startplanappliedroot migration next_in_appnoderrrmigration_plansB     z MigrationExecutor.migration_plancsjttjjd}|r3jjjdd}fddjjD}|D]\}}||vr2|j|ddq#|S)z Create a project state including all the applications without migrations and applied migrations if with_applied_migrations=True. ) real_appsTr&c(h|]}|jjjvrjjj|qSrr rrrkeyrrr I  z:MigrationExecutor._create_project_state..Fpreserve) r listr unmigrated_appsr-r leaf_nodesr mutate_state)rwith_applied_migrationsstate full_planrr*_rr4r_create_project_state@s  z'MigrationExecutor._create_project_statec Cs|j|dur||}|j|jjdd}tdd|D}tdd|D}|s8|dur7|jdd}n(||krAtd||rX|durM|jdd}|j |||||d }n|j |||d }| |S) z Migrate the database up to the given targets. Django first needs to create all project states before a migration is (un)applied and in a second step run all the database operations. NTr/css|]\}}| VqdSr rrmig backwardsrrrrbsz,MigrationExecutor.migrate..css|]\}}|VqdSr rrBrrrrc)r=zMigration plans with both forwards and backwards migrations are not supported. Please split your migration process into separate plans of only forwards OR backwards migrations.fake fake_initialrG) r ensure_schemar-r rr;allrAr_migrate_all_forwards_migrate_all_backwardscheck_replacements) rr%r'r>rGrHr? all_forwards all_backwardsrrrmigrateRs,    zMigrationExecutor.migratec Cs~dd|D}|D]3\}}|s|S||vr.r render_startrender_successrF)__dict__rrapply_migrationremove) rr>r'r?rGrHmigrations_to_runr*r@rrrrLs       z'MigrationExecutor._migrate_all_forwardsc sJdd|D}i}}fddjjD}jr d|D]0\}} |s*n)||vrGd|jvr6|j|||<|j|dd}||q"||vrR|j|ddq"jr[d |D]\}} j||||d ||q]|d d } || }t |D]#\} \}} || kr|| d D]\}} ||vr|j|ddq|Sq|S)a Take a list of 2-tuples of the form (migration instance, True) and unapply them in reverse order they occur in the full_plan. Since unapplying a migration requires the project state prior to that migration, Django will compute the migration states before each of them in a first run over the plan and then unapply them in a second run over the plan. cSrRrSrrTrrrr5rVz;MigrationExecutor._migrate_all_backwards..cr0rr1r2r4rrr5r6rWrTr7FrXrIrN) rAr rrrYrr<r[unapply_migration enumerate) rr'r?rGr\statesr>rr*r@last_unapplied_migrationindexrr4rrMsJ          z(MigrationExecutor._migrate_all_backwardscCsd}|jr |d|||sF|r|||\}}|rd}|sF|jj|jd}|||}|js7||d}Wdn1sAwY|sM|||jrW|d|||S)zRun a migration forwards.F apply_startTatomicN apply_success)rdetect_soft_appliedr schema_editorreapply deferred_sqlrecord_migration)rr>r*rGrHmigration_recordedr(rhrrrrZs*   z!MigrationExecutor.apply_migrationcCs>|jr|jD] \}}|j||qdS|j|j|jdSr )replacesrrecord_applied app_labelname)rr*rorprrrrks z"MigrationExecutor.record_migrationcCs|jr |d|||s*|jj|jd}|||}Wdn1s%wY|jr=|jD] \}}|j||q0n |j|j|j |jrP|d|||S)zRun a migration backwards. unapply_startrdNunapply_success) rr rhreunapplyrmrrecord_unappliedrorp)rr>r*rGrhrorprrrr^sz#MigrationExecutor.unapply_migrationcsT|j|jjD]\}}tfdd|jD}|r'|vr'|jj|q dS)a Mark replacement migrations applied if their replaced set all are. Do this unconditionally on every migrate, rather than just when migrations are applied or unapplied, to correctly handle the case when a new squash migration is pushed to a deployment that already had all its replaced migrations applied. In this case no new migration will be applied, but the applied state of the squashed migration must be maintained. c3s|]}|vVqdSr rrTr(rrrrEz7MigrationExecutor.check_replacements..N)rrr replacementsitemsrKrmrn)rr3r* all_appliedrrurrNs   z$MigrationExecutor.check_replacementsc sxfdd}jdurtfddjDrd|fSn jdur%d|fS|dur6jjjjfdd}n|}|j}d}d}j j j }j } t j j| } |r_d d | D} Wdn1siwYjD]} t| tjr|j| j} | jjrt| jj} || rqq| jj} |r| } | | vrd|fSd}qqt| tjr4|j| j} | jjrt| jj} || rqq| jj}| j| j}|jr|jjjj}|r|}|| vrd|fSd}qqj } j j | |}Wdn 1s wY|D]}|j!}|j}|r#|}|}||kr,d}nqd|fSqq|p9||fS) z Test whether a migration has been implicitly applied - that the tables or columns it would create exist. This is intended only for use on initial migrations (as it only looks for CreateModel and AddField). cs.|jjp|jj ptjjj|j|jjd S)z No need to detect tables for proxy models, unmanaged models, or models that can't be migrated on the current database. ) model_name) _metaproxymanagedr allow_migrater aliasrory)r*modelr4rrshould_skip_detecting_models zJMigrationExecutor.detect_soft_applied..should_skip_detecting_modelNc3s|] \}}|jkVqdSr )ro)rapprp)r*rrr.sz8MigrationExecutor.detect_soft_applied..FT)at_endcSsh|]}|qSr)casefold)rrprrrr5?rVz8MigrationExecutor.detect_soft_applied..)"initialany dependenciesr project_staterorpr<rr featuresignores_table_name_casecursorset introspection table_names operations isinstancer CreateModel get_modelrzswapped global_appsdb_tablerAddFieldry get_field many_to_many remote_fieldthroughget_table_descriptioncolumn)rrr*r after_staterfound_create_model_migrationfound_add_field_migrationfold_identifier_caserexisting_table_names operationrrtablefieldthrough_db_tablecolumnsr field_column column_namer)r*rrrgs                z%MigrationExecutor.detect_soft_appliedr )F)NNFF)FF)__name__ __module__ __qualname____doc__rr-rArQrLrMrZrkr^rNrgrrrrr s   * - =  r N)django.apps.registryrr django.dbrr exceptionsrr rrrr>r r rrrrs