Ticket #24899: 0001-Fixes-24899.patch

File 0001-Fixes-24899.patch, 9.0 KB (added by Steadman, 9 years ago)

Proposed patch (passing all migration tests)

  • django/db/migrations/autodetector.py

    From 5598705b30a9dba008cd36018dcbccca48f7899b Mon Sep 17 00:00:00 2001
    From: Steadman <mark@steadman.io>
    Date: Thu, 4 Jun 2015 14:47:44 +0100
    Subject: [PATCH] Fixes #24899
    
    ---
     django/db/migrations/autodetector.py | 127 ++++++++++++++++++++---------------
     1 file changed, 71 insertions(+), 56 deletions(-)
    
    diff --git a/django/db/migrations/autodetector.py b/django/db/migrations/autodetector.py
    index 9a4ba42..bf23980 100644
    a b class MigrationAutodetector(object):  
    155155        # Renames have to come first
    156156        self.generate_renamed_models()
    157157
    158         # Prepare field lists, and prepare a list of the fields that used
    159         # through models in the old state so we can make dependencies
    160         # from the through model deletion to the field that uses it.
    161         self.kept_model_keys = set(self.old_model_keys).intersection(self.new_model_keys)
    162         self.kept_proxy_keys = set(self.old_proxy_keys).intersection(self.new_proxy_keys)
    163         self.kept_unmanaged_keys = set(self.old_unmanaged_keys).intersection(self.new_unmanaged_keys)
    164         self.through_users = {}
    165         self.old_field_keys = set()
    166         self.new_field_keys = set()
    167         for app_label, model_name in sorted(self.kept_model_keys):
    168             old_model_name = self.renamed_models.get((app_label, model_name), model_name)
    169             old_model_state = self.from_state.models[app_label, old_model_name]
    170             new_model_state = self.to_state.models[app_label, model_name]
    171             self.old_field_keys.update((app_label, model_name, x) for x, y in old_model_state.fields)
    172             self.new_field_keys.update((app_label, model_name, x) for x, y in new_model_state.fields)
    173 
    174         # Through model map generation
    175         for app_label, model_name in sorted(self.old_model_keys):
    176             old_model_name = self.renamed_models.get((app_label, model_name), model_name)
    177             old_model_state = self.from_state.models[app_label, old_model_name]
    178             for field_name, field in old_model_state.fields:
    179                 old_field = self.old_apps.get_model(app_label, old_model_name)._meta.get_field(field_name)
    180                 if (hasattr(old_field, "remote_field") and getattr(old_field.remote_field, "through", None)
    181                         and not old_field.remote_field.through._meta.auto_created):
    182                     through_key = (
    183                         old_field.remote_field.through._meta.app_label,
    184                         old_field.remote_field.through._meta.model_name,
    185                     )
    186                     self.through_users[through_key] = (app_label, old_model_name, field_name)
     158        # Prepare lists of fields and generate through model map
     159        self._prepare_field_lists()
     160        self._generate_through_model_map()
    187161
    188162        # Generate non-rename model operations
    189163        self.generate_deleted_models()
    class MigrationAutodetector(object):  
    207181        # isn't bad, but we need to pull a few things around so FKs work nicely
    208182        # inside the same app
    209183        for app_label, ops in sorted(self.generated_operations.items()):
    210 
    211184            # construct a dependency graph for intra-app dependencies
    212185            dependency_graph = {op: set() for op in ops}
    213186            for op in ops:
    class MigrationAutodetector(object):  
    220193            # we use a stable sort for deterministic tests & general behavior
    221194            self.generated_operations[app_label] = stable_topological_sort(ops, dependency_graph)
    222195
    223         # Now, we need to chop the lists of operations up into migrations with
    224         # dependencies on each other.
    225         # We do this by stepping up an app's list of operations until we
    226         # find one that has an outgoing dependency that isn't in another app's
    227         # migration yet (hasn't been chopped off its list). We then chop off the
    228         # operations before it into a migration and move onto the next app.
    229         # If we loop back around without doing anything, there's a circular
    230         # dependency (which _should_ be impossible as the operations are all
    231         # split at this point so they can't depend and be depended on)
     196        self._build_migration_list(graph)
     197
     198        # OK, add in internal dependencies among the migrations
     199        for app_label, migrations in self.migrations.items():
     200            for m1, m2 in zip(migrations, migrations[1:]):
     201                m2.dependencies.append((app_label, m1.name))
     202
     203        # De-dupe dependencies
     204        for app_label, migrations in self.migrations.items():
     205            for migration in migrations:
     206                migration.dependencies = list(set(migration.dependencies))
     207
     208        # Optimize migrations
     209        for app_label, migrations in self.migrations.items():
     210            for migration in migrations:
     211                migration.operations = MigrationOptimizer().optimize(migration.operations, app_label=app_label)
     212
     213        return self.migrations
     214
     215    def _prepare_field_lists(self):
     216        """
     217        Prepare field lists, and prepare a list of the fields that used
     218        through models in the old state so we can make dependencies
     219        from the through model deletion to the field that uses it.
     220        """
     221
     222        self.kept_model_keys = set(self.old_model_keys).intersection(self.new_model_keys)
     223        self.kept_proxy_keys = set(self.old_proxy_keys).intersection(self.new_proxy_keys)
     224        self.kept_unmanaged_keys = set(self.old_unmanaged_keys).intersection(self.new_unmanaged_keys)
     225        self.through_users = {}
     226        self.old_field_keys = set()
     227        self.new_field_keys = set()
     228        for app_label, model_name in sorted(self.kept_model_keys):
     229            old_model_name = self.renamed_models.get((app_label, model_name), model_name)
     230            old_model_state = self.from_state.models[app_label, old_model_name]
     231            new_model_state = self.to_state.models[app_label, model_name]
     232            self.old_field_keys.update((app_label, model_name, x) for x, y in old_model_state.fields)
     233            self.new_field_keys.update((app_label, model_name, x) for x, y in new_model_state.fields)
     234
     235    def _generate_through_model_map(self):
     236        """
     237        Through model map generation
     238        """
     239
     240        for app_label, model_name in sorted(self.old_model_keys):
     241            old_model_name = self.renamed_models.get((app_label, model_name), model_name)
     242            old_model_state = self.from_state.models[app_label, old_model_name]
     243            for field_name, field in old_model_state.fields:
     244                old_field = self.old_apps.get_model(app_label, old_model_name)._meta.get_field(field_name)
     245                if (hasattr(old_field, "remote_field") and getattr(old_field.remote_field, "through", None)
     246                        and not old_field.remote_field.through._meta.auto_created):
     247                    through_key = (
     248                        old_field.remote_field.through._meta.app_label,
     249                        old_field.remote_field.through._meta.model_name,
     250                    )
     251                    self.through_users[through_key] = (app_label, old_model_name, field_name)
     252
     253    def _build_migration_list(self, graph=None):
     254        """
     255        We need to chop the lists of operations up into migrations with
     256        dependencies on each other. We do this by stepping up an app's list of
     257        operations until we find one that has an outgoing dependency that isn't
     258        in another app's migration yet (hasn't been chopped off its list). We
     259        then chop off the operations before it into a migration and move onto
     260        the next app. If we loop back around without doing anything, there's a
     261        circular dependency (which _should_ be impossible as the operations are
     262        all split at this point so they can't depend and be depended on).
     263        """
    232264
    233265        self.migrations = {}
    234266        num_ops = sum(len(x) for x in self.generated_operations.values())
    class MigrationAutodetector(object):  
    309341                    raise ValueError("Cannot resolve operation dependencies: %r" % self.generated_operations)
    310342            num_ops = new_num_ops
    311343
    312         # OK, add in internal dependencies among the migrations
    313         for app_label, migrations in self.migrations.items():
    314             for m1, m2 in zip(migrations, migrations[1:]):
    315                 m2.dependencies.append((app_label, m1.name))
    316 
    317         # De-dupe dependencies
    318         for app_label, migrations in self.migrations.items():
    319             for migration in migrations:
    320                 migration.dependencies = list(set(migration.dependencies))
    321 
    322         # Optimize migrations
    323         for app_label, migrations in self.migrations.items():
    324             for migration in migrations:
    325                 migration.operations = MigrationOptimizer().optimize(migration.operations, app_label=app_label)
    326 
    327         return self.migrations
    328 
    329344    def check_dependency(self, operation, dependency):
    330345        """
    331346        Returns ``True`` if the given operation depends on the given dependency,
Back to Top