mirror of
				https://github.com/django/django.git
				synced 2025-11-04 05:35:37 +00:00 
			
		
		
		
	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 changed files with 49 additions and 25 deletions
				
			
		| 
						 | 
					@ -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…
	
	Add table
		Add a link
		
	
		Reference in a new issue