Loading django/db/migrations/state.py +33 −25 Original line number Diff line number Diff line Loading @@ -2,6 +2,7 @@ from __future__ import unicode_literals import copy from collections import OrderedDict from contextlib import contextmanager from django.apps import AppConfig from django.apps.registry import Apps, apps as global_apps Loading Loading @@ -111,11 +112,9 @@ class ProjectState(object): related_models.add((app_label, model_name)) # Unregister all related models with self.apps.bulk_update(): for rel_app_label, rel_model_name in related_models: self.apps.unregister_model(rel_app_label, rel_model_name) # Need to do it once all models are unregistered to avoid corrupting # existing models' _meta self.apps.clear_cache() states_to_be_rendered = [] # Gather all models states of those models that will be rerendered. Loading Loading @@ -226,6 +225,18 @@ class StateApps(Apps): labels = (".".join(model_key) for model_key in self._pending_operations) raise ValueError(msg % ", ".join(labels)) @contextmanager def bulk_update(self): # Avoid clearing each model's cache for each change. Instead, clear # all caches when we're finished updating the model instances. ready = self.ready self.ready = False try: yield finally: self.ready = ready self.clear_cache() def render_multiple(self, model_states): # We keep trying to render the models in a loop, ignoring invalid # base errors, until the size of the unrendered models doesn't Loading @@ -234,7 +245,7 @@ class StateApps(Apps): if not model_states: return # Prevent that all model caches are expired for each render. self.ready = False with self.bulk_update(): unrendered_models = model_states while unrendered_models: new_unrendered_models = [] Loading @@ -244,7 +255,6 @@ class StateApps(Apps): except InvalidBasesError: new_unrendered_models.append(model) if len(new_unrendered_models) == len(unrendered_models): self.ready = True raise InvalidBasesError( "Cannot resolve bases for %r\nThis can happen if you are inheriting models from an " "app with migrations (e.g. contrib.auth)\n in an app with no migrations; see " Loading @@ -252,8 +262,6 @@ class StateApps(Apps): "for more" % (new_unrendered_models, get_docs_version()) ) unrendered_models = new_unrendered_models self.ready = True self.clear_cache() def clone(self): """ Loading tests/migrations/test_state.py +16 −0 Original line number Diff line number Diff line Loading @@ -160,6 +160,22 @@ class StateTests(TestCase): self.assertEqual([mgr.args for name, mgr in food_order_manager_state.managers], [('a', 'b', 1, 2), ('x', 'y', 3, 4)]) def test_apps_bulk_update(self): """ StateApps.bulk_update() should update apps.ready to False and reset the value afterwards. """ project_state = ProjectState() apps = project_state.apps with apps.bulk_update(): self.assertFalse(apps.ready) self.assertTrue(apps.ready) with self.assertRaises(ValueError): with apps.bulk_update(): self.assertFalse(apps.ready) raise ValueError() self.assertTrue(apps.ready) def test_render(self): """ Tests rendering a ProjectState into an Apps. Loading Loading
django/db/migrations/state.py +33 −25 Original line number Diff line number Diff line Loading @@ -2,6 +2,7 @@ from __future__ import unicode_literals import copy from collections import OrderedDict from contextlib import contextmanager from django.apps import AppConfig from django.apps.registry import Apps, apps as global_apps Loading Loading @@ -111,11 +112,9 @@ class ProjectState(object): related_models.add((app_label, model_name)) # Unregister all related models with self.apps.bulk_update(): for rel_app_label, rel_model_name in related_models: self.apps.unregister_model(rel_app_label, rel_model_name) # Need to do it once all models are unregistered to avoid corrupting # existing models' _meta self.apps.clear_cache() states_to_be_rendered = [] # Gather all models states of those models that will be rerendered. Loading Loading @@ -226,6 +225,18 @@ class StateApps(Apps): labels = (".".join(model_key) for model_key in self._pending_operations) raise ValueError(msg % ", ".join(labels)) @contextmanager def bulk_update(self): # Avoid clearing each model's cache for each change. Instead, clear # all caches when we're finished updating the model instances. ready = self.ready self.ready = False try: yield finally: self.ready = ready self.clear_cache() def render_multiple(self, model_states): # We keep trying to render the models in a loop, ignoring invalid # base errors, until the size of the unrendered models doesn't Loading @@ -234,7 +245,7 @@ class StateApps(Apps): if not model_states: return # Prevent that all model caches are expired for each render. self.ready = False with self.bulk_update(): unrendered_models = model_states while unrendered_models: new_unrendered_models = [] Loading @@ -244,7 +255,6 @@ class StateApps(Apps): except InvalidBasesError: new_unrendered_models.append(model) if len(new_unrendered_models) == len(unrendered_models): self.ready = True raise InvalidBasesError( "Cannot resolve bases for %r\nThis can happen if you are inheriting models from an " "app with migrations (e.g. contrib.auth)\n in an app with no migrations; see " Loading @@ -252,8 +262,6 @@ class StateApps(Apps): "for more" % (new_unrendered_models, get_docs_version()) ) unrendered_models = new_unrendered_models self.ready = True self.clear_cache() def clone(self): """ Loading
tests/migrations/test_state.py +16 −0 Original line number Diff line number Diff line Loading @@ -160,6 +160,22 @@ class StateTests(TestCase): self.assertEqual([mgr.args for name, mgr in food_order_manager_state.managers], [('a', 'b', 1, 2), ('x', 'y', 3, 4)]) def test_apps_bulk_update(self): """ StateApps.bulk_update() should update apps.ready to False and reset the value afterwards. """ project_state = ProjectState() apps = project_state.apps with apps.bulk_update(): self.assertFalse(apps.ready) self.assertTrue(apps.ready) with self.assertRaises(ValueError): with apps.bulk_update(): self.assertFalse(apps.ready) raise ValueError() self.assertTrue(apps.ready) def test_render(self): """ Tests rendering a ProjectState into an Apps. Loading