Loading django/db/migrations/operations/models.py +12 −10 Original line number Diff line number Diff line Loading @@ -104,19 +104,21 @@ class CreateModel(ModelOperation): return "Create %smodel %s" % ("proxy " if self.options.get("proxy", False) else "", self.name) def references_model(self, name, app_label=None): strings_to_check = [self.name] name_lower = name.lower() if name_lower == self.name_lower: return True # Check we didn't inherit from the model for base in self.bases: if isinstance(base, six.string_types): strings_to_check.append(base.split(".")[-1]) models_to_check = [base for base in self.bases if base is not models.Model] # Check we have no FKs/M2Ms with it for fname, field in self.fields: if field.remote_field: if isinstance(field.remote_field.model, six.string_types): strings_to_check.append(field.remote_field.model.split(".")[-1]) # Now go over all the strings and compare them for string in strings_to_check: if string.lower() == name.lower(): models_to_check.append(field.remote_field.model) # Now go over all the models and check against them for model in models_to_check: model_app_label, model_name = self.model_to_key(model) if model_name.lower() == name_lower: if app_label is None or not model_app_label or model_app_label == app_label: return True return False Loading tests/migrations/test_optimizer.py +51 −6 Original line number Diff line number Diff line Loading @@ -13,15 +13,15 @@ class OptimizerTests(SimpleTestCase): Tests the migration autodetector. """ def optimize(self, operations): def optimize(self, operations, app_label): """ Handy shortcut for getting results + number of loops """ optimizer = MigrationOptimizer() return optimizer.optimize(operations), optimizer._iterations return optimizer.optimize(operations, app_label), optimizer._iterations def assertOptimizesTo(self, operations, expected, exact=None, less_than=None): result, iterations = self.optimize(operations) def assertOptimizesTo(self, operations, expected, exact=None, less_than=None, app_label=None): result, iterations = self.optimize(operations, app_label) result = [repr(f.deconstruct()) for f in result] expected = [repr(f.deconstruct()) for f in expected] self.assertEqual(expected, result) Loading @@ -34,8 +34,8 @@ class OptimizerTests(SimpleTestCase): "Optimization did not take less than %s iterations (it took %s)" % (less_than, iterations) ) def assertDoesNotOptimize(self, operations): self.assertOptimizesTo(operations, operations) def assertDoesNotOptimize(self, operations, **kwargs): self.assertOptimizesTo(operations, operations, **kwargs) def test_single(self): """ Loading Loading @@ -212,6 +212,29 @@ class OptimizerTests(SimpleTestCase): migrations.DeleteModel("Foo"), ], ) # The same operations should be optimized if app_label is specified and # a FK references a model from the other app. self.assertOptimizesTo( [ migrations.CreateModel("Foo", [("name", models.CharField(max_length=255))]), migrations.CreateModel("Bar", [("other", models.ForeignKey("testapp.Foo", models.CASCADE))]), migrations.DeleteModel("Foo"), ], [ migrations.CreateModel("Bar", [("other", models.ForeignKey("testapp.Foo", models.CASCADE))]), ], app_label="otherapp", ) # But it shouldn't work if a FK references a model with the same # app_label. self.assertDoesNotOptimize( [ migrations.CreateModel("Foo", [("name", models.CharField(max_length=255))]), migrations.CreateModel("Bar", [("other", models.ForeignKey("testapp.Foo", models.CASCADE))]), migrations.DeleteModel("Foo"), ], app_label="testapp", ) # This should not work - bases should block it self.assertOptimizesTo( [ Loading @@ -225,6 +248,28 @@ class OptimizerTests(SimpleTestCase): migrations.DeleteModel("Foo"), ], ) # The same operations should be optimized if app_label and none of # bases belong to that app. self.assertOptimizesTo( [ migrations.CreateModel("Foo", [("name", models.CharField(max_length=255))]), migrations.CreateModel("Bar", [("size", models.IntegerField())], bases=("testapp.Foo", )), migrations.DeleteModel("Foo"), ], [ migrations.CreateModel("Bar", [("size", models.IntegerField())], bases=("testapp.Foo", )), ], app_label="otherapp", ) # But it shouldn't work if some of bases belongs to the specified app. self.assertDoesNotOptimize( [ migrations.CreateModel("Foo", [("name", models.CharField(max_length=255))]), migrations.CreateModel("Bar", [("size", models.IntegerField())], bases=("testapp.Foo", )), migrations.DeleteModel("Foo"), ], app_label="testapp", ) def test_create_model_add_field(self): """ Loading Loading
django/db/migrations/operations/models.py +12 −10 Original line number Diff line number Diff line Loading @@ -104,19 +104,21 @@ class CreateModel(ModelOperation): return "Create %smodel %s" % ("proxy " if self.options.get("proxy", False) else "", self.name) def references_model(self, name, app_label=None): strings_to_check = [self.name] name_lower = name.lower() if name_lower == self.name_lower: return True # Check we didn't inherit from the model for base in self.bases: if isinstance(base, six.string_types): strings_to_check.append(base.split(".")[-1]) models_to_check = [base for base in self.bases if base is not models.Model] # Check we have no FKs/M2Ms with it for fname, field in self.fields: if field.remote_field: if isinstance(field.remote_field.model, six.string_types): strings_to_check.append(field.remote_field.model.split(".")[-1]) # Now go over all the strings and compare them for string in strings_to_check: if string.lower() == name.lower(): models_to_check.append(field.remote_field.model) # Now go over all the models and check against them for model in models_to_check: model_app_label, model_name = self.model_to_key(model) if model_name.lower() == name_lower: if app_label is None or not model_app_label or model_app_label == app_label: return True return False Loading
tests/migrations/test_optimizer.py +51 −6 Original line number Diff line number Diff line Loading @@ -13,15 +13,15 @@ class OptimizerTests(SimpleTestCase): Tests the migration autodetector. """ def optimize(self, operations): def optimize(self, operations, app_label): """ Handy shortcut for getting results + number of loops """ optimizer = MigrationOptimizer() return optimizer.optimize(operations), optimizer._iterations return optimizer.optimize(operations, app_label), optimizer._iterations def assertOptimizesTo(self, operations, expected, exact=None, less_than=None): result, iterations = self.optimize(operations) def assertOptimizesTo(self, operations, expected, exact=None, less_than=None, app_label=None): result, iterations = self.optimize(operations, app_label) result = [repr(f.deconstruct()) for f in result] expected = [repr(f.deconstruct()) for f in expected] self.assertEqual(expected, result) Loading @@ -34,8 +34,8 @@ class OptimizerTests(SimpleTestCase): "Optimization did not take less than %s iterations (it took %s)" % (less_than, iterations) ) def assertDoesNotOptimize(self, operations): self.assertOptimizesTo(operations, operations) def assertDoesNotOptimize(self, operations, **kwargs): self.assertOptimizesTo(operations, operations, **kwargs) def test_single(self): """ Loading Loading @@ -212,6 +212,29 @@ class OptimizerTests(SimpleTestCase): migrations.DeleteModel("Foo"), ], ) # The same operations should be optimized if app_label is specified and # a FK references a model from the other app. self.assertOptimizesTo( [ migrations.CreateModel("Foo", [("name", models.CharField(max_length=255))]), migrations.CreateModel("Bar", [("other", models.ForeignKey("testapp.Foo", models.CASCADE))]), migrations.DeleteModel("Foo"), ], [ migrations.CreateModel("Bar", [("other", models.ForeignKey("testapp.Foo", models.CASCADE))]), ], app_label="otherapp", ) # But it shouldn't work if a FK references a model with the same # app_label. self.assertDoesNotOptimize( [ migrations.CreateModel("Foo", [("name", models.CharField(max_length=255))]), migrations.CreateModel("Bar", [("other", models.ForeignKey("testapp.Foo", models.CASCADE))]), migrations.DeleteModel("Foo"), ], app_label="testapp", ) # This should not work - bases should block it self.assertOptimizesTo( [ Loading @@ -225,6 +248,28 @@ class OptimizerTests(SimpleTestCase): migrations.DeleteModel("Foo"), ], ) # The same operations should be optimized if app_label and none of # bases belong to that app. self.assertOptimizesTo( [ migrations.CreateModel("Foo", [("name", models.CharField(max_length=255))]), migrations.CreateModel("Bar", [("size", models.IntegerField())], bases=("testapp.Foo", )), migrations.DeleteModel("Foo"), ], [ migrations.CreateModel("Bar", [("size", models.IntegerField())], bases=("testapp.Foo", )), ], app_label="otherapp", ) # But it shouldn't work if some of bases belongs to the specified app. self.assertDoesNotOptimize( [ migrations.CreateModel("Foo", [("name", models.CharField(max_length=255))]), migrations.CreateModel("Bar", [("size", models.IntegerField())], bases=("testapp.Foo", )), migrations.DeleteModel("Foo"), ], app_label="testapp", ) def test_create_model_add_field(self): """ Loading