mirror of https://github.com/django/django.git
Refs #24397 -- Sped up model reloading in ProjectState.
Created bulk_update() context manager on StateApps. Sped up unregistering models in reload_models() by using this context mananger.
This commit is contained in:
parent
6387d9d41f
commit
039d7881b4
|
@ -2,6 +2,7 @@ from __future__ import unicode_literals
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
from contextlib import contextmanager
|
||||||
|
|
||||||
from django.apps import AppConfig
|
from django.apps import AppConfig
|
||||||
from django.apps.registry import Apps, apps as global_apps
|
from django.apps.registry import Apps, apps as global_apps
|
||||||
|
@ -111,11 +112,9 @@ class ProjectState(object):
|
||||||
related_models.add((app_label, model_name))
|
related_models.add((app_label, model_name))
|
||||||
|
|
||||||
# Unregister all related models
|
# Unregister all related models
|
||||||
for rel_app_label, rel_model_name in related_models:
|
with self.apps.bulk_update():
|
||||||
self.apps.unregister_model(rel_app_label, rel_model_name)
|
for rel_app_label, rel_model_name in related_models:
|
||||||
# Need to do it once all models are unregistered to avoid corrupting
|
self.apps.unregister_model(rel_app_label, rel_model_name)
|
||||||
# existing models' _meta
|
|
||||||
self.apps.clear_cache()
|
|
||||||
|
|
||||||
states_to_be_rendered = []
|
states_to_be_rendered = []
|
||||||
# Gather all models states of those models that will be rerendered.
|
# Gather all models states of those models that will be rerendered.
|
||||||
|
@ -226,6 +225,18 @@ class StateApps(Apps):
|
||||||
labels = (".".join(model_key) for model_key in self._pending_operations)
|
labels = (".".join(model_key) for model_key in self._pending_operations)
|
||||||
raise ValueError(msg % ", ".join(labels))
|
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):
|
def render_multiple(self, model_states):
|
||||||
# We keep trying to render the models in a loop, ignoring invalid
|
# We keep trying to render the models in a loop, ignoring invalid
|
||||||
# base errors, until the size of the unrendered models doesn't
|
# base errors, until the size of the unrendered models doesn't
|
||||||
|
@ -234,26 +245,23 @@ class StateApps(Apps):
|
||||||
if not model_states:
|
if not model_states:
|
||||||
return
|
return
|
||||||
# Prevent that all model caches are expired for each render.
|
# Prevent that all model caches are expired for each render.
|
||||||
self.ready = False
|
with self.bulk_update():
|
||||||
unrendered_models = model_states
|
unrendered_models = model_states
|
||||||
while unrendered_models:
|
while unrendered_models:
|
||||||
new_unrendered_models = []
|
new_unrendered_models = []
|
||||||
for model in unrendered_models:
|
for model in unrendered_models:
|
||||||
try:
|
try:
|
||||||
model.render(self)
|
model.render(self)
|
||||||
except InvalidBasesError:
|
except InvalidBasesError:
|
||||||
new_unrendered_models.append(model)
|
new_unrendered_models.append(model)
|
||||||
if len(new_unrendered_models) == len(unrendered_models):
|
if len(new_unrendered_models) == len(unrendered_models):
|
||||||
self.ready = True
|
raise InvalidBasesError(
|
||||||
raise InvalidBasesError(
|
"Cannot resolve bases for %r\nThis can happen if you are inheriting models from an "
|
||||||
"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 "
|
||||||
"app with migrations (e.g. contrib.auth)\n in an app with no migrations; see "
|
"https://docs.djangoproject.com/en/%s/topics/migrations/#dependencies "
|
||||||
"https://docs.djangoproject.com/en/%s/topics/migrations/#dependencies "
|
"for more" % (new_unrendered_models, get_docs_version())
|
||||||
"for more" % (new_unrendered_models, get_docs_version())
|
)
|
||||||
)
|
unrendered_models = new_unrendered_models
|
||||||
unrendered_models = new_unrendered_models
|
|
||||||
self.ready = True
|
|
||||||
self.clear_cache()
|
|
||||||
|
|
||||||
def clone(self):
|
def clone(self):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -160,6 +160,22 @@ class StateTests(TestCase):
|
||||||
self.assertEqual([mgr.args for name, mgr in food_order_manager_state.managers],
|
self.assertEqual([mgr.args for name, mgr in food_order_manager_state.managers],
|
||||||
[('a', 'b', 1, 2), ('x', 'y', 3, 4)])
|
[('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):
|
def test_render(self):
|
||||||
"""
|
"""
|
||||||
Tests rendering a ProjectState into an Apps.
|
Tests rendering a ProjectState into an Apps.
|
||||||
|
|
Loading…
Reference in New Issue