Fixed #22875: Optimizer did not take through= into account.

This commit is contained in:
Andrew Godwin 2014-06-22 11:23:45 -07:00
parent f07735c619
commit b30d32ff24
2 changed files with 58 additions and 5 deletions

View File

@ -160,6 +160,19 @@ class MigrationOptimizer(object):
return om(operation, other, in_between or []) return om(operation, other, in_between or [])
return None 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): def reduce_model_create_delete(self, operation, other, in_between):
""" """
Folds a CreateModel and a DeleteModel into nothing. Folds a CreateModel and a DeleteModel into nothing.
@ -206,13 +219,15 @@ class MigrationOptimizer(object):
# Don't allow optimisations of FKs through models they reference # Don't allow optimisations of FKs through models they reference
if hasattr(other.field, "rel") and other.field.rel: if hasattr(other.field, "rel") and other.field.rel:
for between in in_between: for between in in_between:
if isinstance(other.field.rel.to, six.string_types): # Check that it doesn't point to the model
object_name, app_label = other.field.rel.to.split(".", 1) app_label, object_name = self.model_to_key(other.field.rel.to)
else:
object_name = other.field.rel.to._meta.object_name
app_label = other.field.rel.to._meta.app_label
if between.references_model(object_name, app_label): if between.references_model(object_name, app_label):
return None 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 # OK, that's fine
return [ return [
migrations.CreateModel( migrations.CreateModel(

View File

@ -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): def test_create_model_alter_field(self):
""" """
AlterField should optimize into CreateModel. AlterField should optimize into CreateModel.