Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Fixed #21893 -- ModelState didn't account for MTI parents inherited f…

…rom abstract models.
  • Loading branch information...
commit 6436f1fad9ce51f18735106ac75aeea3d6d1f310 1 parent 99291f5
Loïc Bistuer loic authored
Showing with 34 additions and 3 deletions.
  1. +19 −3 django/db/migrations/state.py
  2. +15 −0 tests/migrations/test_state.py
22 django/db/migrations/state.py
View
@@ -151,6 +151,23 @@ def from_model(cls, model):
options[name] = set(normalize_together(it))
else:
options[name] = model._meta.original_attrs[name]
+
+ def flatten_bases(model):
+ bases = []
+ for base in model.__bases__:
+ if hasattr(base, "_meta") and base._meta.abstract:
+ bases.extend(flatten_bases(base))
+ else:
+ bases.append(base)
+ return bases
+
+ # We can't rely on __mro__ directly because we only want to flatten
+ # abstract models and not the whole tree. However by recursing on
+ # __bases__ we may end up with duplicates and ordering issues, we
+ # therefore discard any duplicates and reorder the bases according
+ # to their index in the MRO.
+ flattened_bases = sorted(set(flatten_bases(model)), key=lambda x:model.__mro__.index(x))
+
# Make our record
bases = tuple(
(
@@ -158,12 +175,11 @@ def from_model(cls, model):
if hasattr(base, "_meta") else
base
)
- for base in model.__bases__
- if (not hasattr(base, "_meta") or not base._meta.abstract)
+ for base in flattened_bases
)
# Ensure at least one base inherits from models.Model
if not any((isinstance(base, six.string_types) or issubclass(base, models.Model)) for base in bases):
- bases = (models.Model, )
+ bases = (models.Model,)
return cls(
model._meta.app_label,
model._meta.object_name,
15 tests/migrations/test_state.py
View
@@ -166,6 +166,16 @@ class Meta:
app_label = "migrations"
apps = Apps()
+ class AbstractSubFooBar(FooBar):
+ class Meta:
+ abstract = True
+ apps = Apps()
+
+ class SubFooBar(AbstractSubFooBar):
+ class Meta:
+ app_label = "migrations"
+ apps = Apps()
+
apps = Apps(["migrations"])
# We shouldn't be able to render yet
@@ -175,8 +185,13 @@ class Meta:
# Once the parent models are in the app registry, it should be fine
ModelState.from_model(Foo).render(apps)
+ self.assertSequenceEqual(ModelState.from_model(Foo).bases, [models.Model])
ModelState.from_model(Bar).render(apps)
+ self.assertSequenceEqual(ModelState.from_model(Bar).bases, [models.Model])
ModelState.from_model(FooBar).render(apps)
+ self.assertSequenceEqual(ModelState.from_model(FooBar).bases, ['migrations.foo', 'migrations.bar'])
+ ModelState.from_model(SubFooBar).render(apps)
+ self.assertSequenceEqual(ModelState.from_model(SubFooBar).bases, ['migrations.foobar'])
def test_render_project_dependencies(self):
"""
Please sign in to comment.
Something went wrong with that request. Please try again.