diff options
| author | Markus Holtermann <info@markusholtermann.eu> | 2015-01-11 20:13:31 +0100 |
|---|---|---|
| committer | Markus Holtermann <info@markusholtermann.eu> | 2015-01-12 18:39:18 +0100 |
| commit | bbbed99f6260a8b3e65cb990e49721b1ce4a441b (patch) | |
| tree | ec9e86f37c218f66fb8e7d1e5340a8ca049dc81c /django | |
| parent | 8f5d6c77b6bbe0390b683cea649de6ad24d80fef (diff) | |
Fixed #24123 -- Used all available migrations to generate the initial migration state
Thanks Collin Anderson for the input when creating the patch and Tim Graham for the review.
Diffstat (limited to 'django')
| -rw-r--r-- | django/db/migrations/executor.py | 32 |
1 files changed, 25 insertions, 7 deletions
diff --git a/django/db/migrations/executor.py b/django/db/migrations/executor.py index a7989c4107..c9ef387d58 100644 --- a/django/db/migrations/executor.py +++ b/django/db/migrations/executor.py @@ -4,6 +4,7 @@ from django.db import migrations from django.apps.registry import apps as global_apps from .loader import MigrationLoader from .recorder import MigrationRecorder +from .state import ProjectState class MigrationExecutor(object): @@ -18,12 +19,15 @@ class MigrationExecutor(object): self.recorder = MigrationRecorder(self.connection) self.progress_callback = progress_callback - def migration_plan(self, targets): + def migration_plan(self, targets, clean_start=False): """ Given a set of targets, returns a list of (Migration instance, backwards?). """ plan = [] - applied = set(self.loader.applied_migrations) + if clean_start: + applied = set() + else: + applied = set(self.loader.applied_migrations) for target in targets: # If the target is (app_label, None), that means unmigrate everything if target[1] is None: @@ -60,17 +64,31 @@ class MigrationExecutor(object): def migrate(self, targets, plan=None, fake=False): """ Migrates 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. """ if plan is None: plan = self.migration_plan(targets) - state = None + migrations_to_run = {m[0] for m in plan} + # Create the forwards plan Django would follow on an empty database + full_plan = self.migration_plan(self.loader.graph.leaf_nodes(), clean_start=True) + # Holds all states right before and right after a migration is applied + # if the migration is being run. + states = {} + state = ProjectState(real_apps=list(self.loader.unmigrated_apps)) + state.apps # Render all real_apps -- performance critical + # Phase 1 -- Store all required states + for migration, _ in full_plan: + if migration in migrations_to_run: + states[migration] = state.clone() + state = migration.mutate_state(state) # state is cloned inside + # Phase 2 -- Run the migrations for migration, backwards in plan: - if state is None: - state = self.loader.project_state((migration.app_label, migration.name), at_end=False) if not backwards: - state = self.apply_migration(state, migration, fake=fake) + self.apply_migration(states[migration], migration, fake=fake) else: - state = self.unapply_migration(state, migration, fake=fake) + self.unapply_migration(states[migration], migration, fake=fake) def collect_sql(self, plan): """ |
