diff options
| author | Markus Holtermann <info@markusholtermann.eu> | 2015-08-22 23:40:34 +1000 |
|---|---|---|
| committer | Markus Holtermann <info@markusholtermann.eu> | 2015-08-27 17:06:21 +1000 |
| commit | e1427cc609fa6ab247501b101cfb3c0092aba55b (patch) | |
| tree | cd3a323b08fcb8301b267ce5aa784da41d2339e2 /django | |
| parent | 91f701f4fc324cd2feb7dbf151338a358ca0ea18 (diff) | |
Fixed #24590 -- Cached calls to swappable_setting.
Moved the lookup in Field.swappable_setting to Apps, and added
an lru_cache to cache the results.
Refs #24743
Thanks Marten Kenbeek for the initial work on the patch. Thanks Aymeric
Augustin and Tim Graham for the review.
Diffstat (limited to 'django')
| -rw-r--r-- | django/apps/registry.py | 22 | ||||
| -rw-r--r-- | django/db/models/fields/related.py | 13 | ||||
| -rw-r--r-- | django/db/models/options.py | 2 | ||||
| -rw-r--r-- | django/test/utils.py | 12 |
4 files changed, 36 insertions, 13 deletions
diff --git a/django/apps/registry.py b/django/apps/registry.py index 269787545d..46c10ef4fb 100644 --- a/django/apps/registry.py +++ b/django/apps/registry.py @@ -260,6 +260,28 @@ class Apps(object): "Model '%s.%s' not registered." % (app_label, model_name)) return model + @lru_cache.lru_cache(maxsize=None) + def get_swappable_settings_name(self, to_string): + """ + For a given model string (e.g. "auth.User"), return the name of the + corresponding settings name if it refers to a swappable model. If the + referred model is not swappable, return None. + + This method is decorated with lru_cache because it's performance + critical when it comes to migrations. Since the swappable settings don't + change after Django has loaded the settings, there is no reason to get + the respective settings attribute over and over again. + """ + for model in self.get_models(include_swapped=True): + swapped = model._meta.swapped + # Is this model swapped out for the model given by to_string? + if swapped and swapped == to_string: + return model._meta.swappable + # Is this model swappable and the one given by to_string? + if model._meta.swappable and model._meta.label == to_string: + return model._meta.swappable + return None + def set_available_apps(self, available): """ Restricts the set of installed apps used by get_app_config[s]. diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py index f61e557aac..c433ce239d 100644 --- a/django/db/models/fields/related.py +++ b/django/db/models/fields/related.py @@ -314,17 +314,8 @@ class RelatedField(Field): if isinstance(self.remote_field.model, six.string_types): to_string = self.remote_field.model else: - to_string = "%s.%s" % ( - self.remote_field.model._meta.app_label, - self.remote_field.model._meta.object_name, - ) - # See if anything swapped/swappable matches - for model in apps.get_models(include_swapped=True): - if model._meta.swapped: - if model._meta.swapped == to_string: - return model._meta.swappable - if ("%s.%s" % (model._meta.app_label, model._meta.object_name)) == to_string and model._meta.swappable: - return model._meta.swappable + to_string = self.remote_field.model._meta.label + return apps.get_swappable_settings_name(to_string) return None def set_attributes_from_rel(self): diff --git a/django/db/models/options.py b/django/db/models/options.py index 1406ab9441..bc5a95e750 100644 --- a/django/db/models/options.py +++ b/django/db/models/options.py @@ -396,7 +396,7 @@ class Options(object): # or as part of validation. return swapped_for - if '%s.%s' % (swapped_label, swapped_object.lower()) not in (None, self.label_lower): + if '%s.%s' % (swapped_label, swapped_object.lower()) != self.label_lower: return swapped_for return None diff --git a/django/test/utils.py b/django/test/utils.py index da77d8ef4e..415f17237b 100644 --- a/django/test/utils.py +++ b/django/test/utils.py @@ -29,7 +29,7 @@ except ImportError: __all__ = ( - 'Approximate', 'ContextList', 'get_runner', + 'Approximate', 'ContextList', 'isolate_lru_cache', 'get_runner', 'modify_settings', 'override_settings', 'requires_tz_support', 'setup_test_environment', 'teardown_test_environment', @@ -504,6 +504,16 @@ def extend_sys_path(*paths): @contextmanager +def isolate_lru_cache(lru_cache_object): + """Clear the cache of an LRU cache object on entering and exiting.""" + lru_cache_object.cache_clear() + try: + yield + finally: + lru_cache_object.cache_clear() + + +@contextmanager def captured_output(stream_name): """Return a context manager used by captured_stdout/stdin/stderr that temporarily replaces the sys stream *stream_name* with a StringIO. |
