From dab83e5ba108c08a04ae400ac5354fd73e26c6f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Bistuer?= Date: Sat, 9 Jul 2016 17:52:52 +0700 Subject: [PATCH] Fixed #26881 -- Fixed duplicate managers in migrations. When both parent and child models had managers with the same name and a migrations opt-in both were added to the migration state. --- django/db/migrations/state.py | 12 ++++++++++-- tests/migrations/test_state.py | 31 +++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/django/db/migrations/state.py b/django/db/migrations/state.py index 74f7475cf86..12ec9ffdb81 100644 --- a/django/db/migrations/state.py +++ b/django/db/migrations/state.py @@ -447,12 +447,19 @@ class ModelState(object): bases = (models.Model,) managers = [] + manager_names = set() default_manager_shim = None for manager in model._meta.managers: - if manager.use_in_migrations: + manager_name = force_text(manager.name) + if manager_name in manager_names: + # Skip overridden managers. + continue + elif manager.use_in_migrations: + # Copy managers usable in migrations. new_manager = copy.copy(manager) new_manager._set_creation_counter() elif manager is model._base_manager or manager is model._default_manager: + # Shim custom managers used as default and base managers. new_manager = models.Manager() new_manager.model = manager.model new_manager.name = manager.name @@ -460,7 +467,8 @@ class ModelState(object): default_manager_shim = new_manager else: continue - managers.append((force_text(manager.name), new_manager)) + manager_names.add(manager_name) + managers.append((manager_name, new_manager)) # Ignore a shimmed default manager called objects if it's the only one. if managers == [('objects', default_manager_shim)]: diff --git a/tests/migrations/test_state.py b/tests/migrations/test_state.py index dae4fabb67d..e955f1c63f3 100644 --- a/tests/migrations/test_state.py +++ b/tests/migrations/test_state.py @@ -210,6 +210,37 @@ class StateTests(SimpleTestCase): author_state = project_state.models['migrations', 'author'] self.assertEqual(author_state.managers, []) + def test_no_duplicate_managers(self): + """ + When a manager is added with `use_in_migrations = True` and a parent + model had a manager with the same name and `use_in_migrations = True`, + the parent's manager shouldn't appear in the model state (#26881). + """ + new_apps = Apps(['migrations']) + + class PersonManager(models.Manager): + use_in_migrations = True + + class Person(models.Model): + objects = PersonManager() + + class Meta: + abstract = True + + class BossManager(PersonManager): + use_in_migrations = True + + class Boss(Person): + objects = BossManager() + + class Meta: + app_label = 'migrations' + apps = new_apps + + project_state = ProjectState.from_apps(new_apps) + boss_state = project_state.models['migrations', 'boss'] + self.assertEqual(boss_state.managers, [('objects', Boss.objects)]) + def test_custom_default_manager(self): new_apps = Apps(['migrations'])