Loading django/db/migrations/optimizer.py +20 −5 Original line number Diff line number Diff line Loading @@ -160,6 +160,19 @@ class MigrationOptimizer(object): return om(operation, other, in_between or []) return None def model_to_key(self, model): """ Takes either a model class or a "appname.ModelName" string and returns (appname, modelname) """ if isinstance(model, six.string_types): return model.split(".", 1) else: return ( model._meta.app_label, model._meta.object_name, ) def reduce_model_create_delete(self, operation, other, in_between): """ Folds a CreateModel and a DeleteModel into nothing. Loading Loading @@ -206,11 +219,13 @@ class MigrationOptimizer(object): # Don't allow optimisations of FKs through models they reference if hasattr(other.field, "rel") and other.field.rel: for between in in_between: if isinstance(other.field.rel.to, six.string_types): object_name, app_label = other.field.rel.to.split(".", 1) else: object_name = other.field.rel.to._meta.object_name app_label = other.field.rel.to._meta.app_label # Check that it doesn't point to the model app_label, object_name = self.model_to_key(other.field.rel.to) if between.references_model(object_name, app_label): return None # Check that it's not through the model if getattr(other.field.rel, "through", None): app_label, object_name = self.model_to_key(other.field.rel.through) if between.references_model(object_name, app_label): return None # OK, that's fine Loading tests/migrations/test_optimizer.py +38 −0 Original line number Diff line number Diff line Loading @@ -201,6 +201,44 @@ class OptimizerTests(TestCase): ], ) def test_create_model_add_field_not_through_fk(self): """ AddField should NOT optimize into CreateModel if it's an FK to a model that's between them. """ self.assertOptimizesTo( [ migrations.CreateModel("Foo", [("name", models.CharField(max_length=255))]), migrations.CreateModel("Link", [("url", models.TextField())]), migrations.AddField("Foo", "link", models.ForeignKey("migrations.Link")), ], [ migrations.CreateModel("Foo", [("name", models.CharField(max_length=255))]), migrations.CreateModel("Link", [("url", models.TextField())]), migrations.AddField("Foo", "link", models.ForeignKey("migrations.Link")), ], ) def test_create_model_add_field_not_through_m2m_through(self): """ AddField should NOT optimize into CreateModel if it's an M2M using a through that's created between them. """ # Note: The middle model is not actually a valid through model, # but that doesn't matter, as we never render it. self.assertOptimizesTo( [ migrations.CreateModel("Foo", [("name", models.CharField(max_length=255))]), migrations.CreateModel("LinkThrough", []), migrations.AddField("Foo", "link", models.ManyToManyField("migrations.Link", through="migrations.LinkThrough")), ], [ migrations.CreateModel("Foo", [("name", models.CharField(max_length=255))]), migrations.CreateModel("LinkThrough", []), migrations.AddField("Foo", "link", models.ManyToManyField("migrations.Link", through="migrations.LinkThrough")), ], ) def test_create_model_alter_field(self): """ AlterField should optimize into CreateModel. Loading Loading
django/db/migrations/optimizer.py +20 −5 Original line number Diff line number Diff line Loading @@ -160,6 +160,19 @@ class MigrationOptimizer(object): return om(operation, other, in_between or []) return None def model_to_key(self, model): """ Takes either a model class or a "appname.ModelName" string and returns (appname, modelname) """ if isinstance(model, six.string_types): return model.split(".", 1) else: return ( model._meta.app_label, model._meta.object_name, ) def reduce_model_create_delete(self, operation, other, in_between): """ Folds a CreateModel and a DeleteModel into nothing. Loading Loading @@ -206,11 +219,13 @@ class MigrationOptimizer(object): # Don't allow optimisations of FKs through models they reference if hasattr(other.field, "rel") and other.field.rel: for between in in_between: if isinstance(other.field.rel.to, six.string_types): object_name, app_label = other.field.rel.to.split(".", 1) else: object_name = other.field.rel.to._meta.object_name app_label = other.field.rel.to._meta.app_label # Check that it doesn't point to the model app_label, object_name = self.model_to_key(other.field.rel.to) if between.references_model(object_name, app_label): return None # Check that it's not through the model if getattr(other.field.rel, "through", None): app_label, object_name = self.model_to_key(other.field.rel.through) if between.references_model(object_name, app_label): return None # OK, that's fine Loading
tests/migrations/test_optimizer.py +38 −0 Original line number Diff line number Diff line Loading @@ -201,6 +201,44 @@ class OptimizerTests(TestCase): ], ) def test_create_model_add_field_not_through_fk(self): """ AddField should NOT optimize into CreateModel if it's an FK to a model that's between them. """ self.assertOptimizesTo( [ migrations.CreateModel("Foo", [("name", models.CharField(max_length=255))]), migrations.CreateModel("Link", [("url", models.TextField())]), migrations.AddField("Foo", "link", models.ForeignKey("migrations.Link")), ], [ migrations.CreateModel("Foo", [("name", models.CharField(max_length=255))]), migrations.CreateModel("Link", [("url", models.TextField())]), migrations.AddField("Foo", "link", models.ForeignKey("migrations.Link")), ], ) def test_create_model_add_field_not_through_m2m_through(self): """ AddField should NOT optimize into CreateModel if it's an M2M using a through that's created between them. """ # Note: The middle model is not actually a valid through model, # but that doesn't matter, as we never render it. self.assertOptimizesTo( [ migrations.CreateModel("Foo", [("name", models.CharField(max_length=255))]), migrations.CreateModel("LinkThrough", []), migrations.AddField("Foo", "link", models.ManyToManyField("migrations.Link", through="migrations.LinkThrough")), ], [ migrations.CreateModel("Foo", [("name", models.CharField(max_length=255))]), migrations.CreateModel("LinkThrough", []), migrations.AddField("Foo", "link", models.ManyToManyField("migrations.Link", through="migrations.LinkThrough")), ], ) def test_create_model_alter_field(self): """ AlterField should optimize into CreateModel. Loading