Loading django/db/migrations/state.py +15 −3 Original line number Diff line number Diff line Loading @@ -400,6 +400,19 @@ class ModelState(object): field_class = import_string(path) yield name, field_class(*args, **kwargs) def construct_managers(self): "Deep-clone the managers using deconstruction" # Sort all managers by their creation counter sorted_managers = sorted(self.managers, key=lambda v: v[1].creation_counter) for mgr_name, manager in sorted_managers: as_manager, manager_path, qs_path, args, kwargs = manager.deconstruct() if as_manager: qs_class = import_string(qs_path) yield mgr_name, qs_class.as_manager() else: manager_class = import_string(manager_path) yield mgr_name, manager_class(*args, **kwargs) def clone(self): "Returns an exact copy of this ModelState" return self.__class__( Loading @@ -408,7 +421,7 @@ class ModelState(object): fields=list(self.construct_fields()), options=dict(self.options), bases=self.bases, managers=self.managers, managers=list(self.construct_managers()), ) def render(self, apps): Loading @@ -431,8 +444,7 @@ class ModelState(object): body['__module__'] = "__fake__" # Restore managers for mgr_name, manager in self.managers: body[mgr_name] = manager body.update(self.construct_managers()) # Then, make a Model object (apps.register_model is called in __new__) return type( Loading tests/migrations/test_state.py +38 −0 Original line number Diff line number Diff line from django.apps.registry import Apps from django.db import models from django.db.migrations.operations import RemoveField from django.db.migrations.state import ProjectState, ModelState, InvalidBasesError from django.test import TestCase Loading Loading @@ -505,6 +506,43 @@ class StateTests(TestCase): ["id", "author"], ) def test_manager_refer_correct_model_version(self): """ #24147 - Tests that managers refer to the correct version of a historical model """ project_state = ProjectState() project_state.add_model(ModelState( app_label="migrations", name="Tag", fields=[ ("id", models.AutoField(primary_key=True)), ("hidden", models.BooleanField()), ], managers=[ ('food_mgr', FoodManager('a', 'b')), ('food_qs', FoodQuerySet.as_manager()), ] )) old_model = project_state.apps.get_model('migrations', 'tag') new_state = project_state.clone() operation = RemoveField("tag", "hidden") operation.state_forwards("migrations", new_state) new_model = new_state.apps.get_model('migrations', 'tag') self.assertIsNot(old_model, new_model) self.assertIs(old_model, old_model.food_mgr.model) self.assertIs(old_model, old_model.food_qs.model) self.assertIs(new_model, new_model.food_mgr.model) self.assertIs(new_model, new_model.food_qs.model) self.assertIsNot(old_model.food_mgr, new_model.food_mgr) self.assertIsNot(old_model.food_qs, new_model.food_qs) self.assertIsNot(old_model.food_mgr.model, new_model.food_mgr.model) self.assertIsNot(old_model.food_qs.model, new_model.food_qs.model) class ModelStateTests(TestCase): def test_custom_model_base(self): Loading Loading
django/db/migrations/state.py +15 −3 Original line number Diff line number Diff line Loading @@ -400,6 +400,19 @@ class ModelState(object): field_class = import_string(path) yield name, field_class(*args, **kwargs) def construct_managers(self): "Deep-clone the managers using deconstruction" # Sort all managers by their creation counter sorted_managers = sorted(self.managers, key=lambda v: v[1].creation_counter) for mgr_name, manager in sorted_managers: as_manager, manager_path, qs_path, args, kwargs = manager.deconstruct() if as_manager: qs_class = import_string(qs_path) yield mgr_name, qs_class.as_manager() else: manager_class = import_string(manager_path) yield mgr_name, manager_class(*args, **kwargs) def clone(self): "Returns an exact copy of this ModelState" return self.__class__( Loading @@ -408,7 +421,7 @@ class ModelState(object): fields=list(self.construct_fields()), options=dict(self.options), bases=self.bases, managers=self.managers, managers=list(self.construct_managers()), ) def render(self, apps): Loading @@ -431,8 +444,7 @@ class ModelState(object): body['__module__'] = "__fake__" # Restore managers for mgr_name, manager in self.managers: body[mgr_name] = manager body.update(self.construct_managers()) # Then, make a Model object (apps.register_model is called in __new__) return type( Loading
tests/migrations/test_state.py +38 −0 Original line number Diff line number Diff line from django.apps.registry import Apps from django.db import models from django.db.migrations.operations import RemoveField from django.db.migrations.state import ProjectState, ModelState, InvalidBasesError from django.test import TestCase Loading Loading @@ -505,6 +506,43 @@ class StateTests(TestCase): ["id", "author"], ) def test_manager_refer_correct_model_version(self): """ #24147 - Tests that managers refer to the correct version of a historical model """ project_state = ProjectState() project_state.add_model(ModelState( app_label="migrations", name="Tag", fields=[ ("id", models.AutoField(primary_key=True)), ("hidden", models.BooleanField()), ], managers=[ ('food_mgr', FoodManager('a', 'b')), ('food_qs', FoodQuerySet.as_manager()), ] )) old_model = project_state.apps.get_model('migrations', 'tag') new_state = project_state.clone() operation = RemoveField("tag", "hidden") operation.state_forwards("migrations", new_state) new_model = new_state.apps.get_model('migrations', 'tag') self.assertIsNot(old_model, new_model) self.assertIs(old_model, old_model.food_mgr.model) self.assertIs(old_model, old_model.food_qs.model) self.assertIs(new_model, new_model.food_mgr.model) self.assertIs(new_model, new_model.food_qs.model) self.assertIsNot(old_model.food_mgr, new_model.food_mgr) self.assertIsNot(old_model.food_qs, new_model.food_qs) self.assertIsNot(old_model.food_mgr.model, new_model.food_mgr.model) self.assertIsNot(old_model.food_qs.model, new_model.food_qs.model) class ModelStateTests(TestCase): def test_custom_model_base(self): Loading