summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Charette <charette.s@gmail.com>2015-12-18 14:49:23 -0500
committerSimon Charette <charette.s@gmail.com>2016-02-26 16:24:28 -0500
commit4701c81df3c32d8232ed733e5848186b1da61769 (patch)
tree989eccff32828dbb59d082e7d3149efc1320111a
parent3b0b1e071d339e76fb8f3ca1b9250c23350d2ee0 (diff)
downloaddjango-4701c81df3c32d8232ed733e5848186b1da61769.tar.gz
[1.8.x] Fixed #26286 -- Prevented content type managers from sharing their cache.
This should prevent managers methods from returning content type instances registered to foreign apps now that these managers are also attached to models created during migration phases. Thanks Tim for the review. Refs #23822. Backport of 3938b3ccaa85f1c366909a4839696007726a09da from master
-rw-r--r--django/contrib/contenttypes/models.py20
-rw-r--r--docs/releases/1.8.10.txt3
-rw-r--r--tests/contenttypes_tests/test_models.py14
3 files changed, 27 insertions, 10 deletions
diff --git a/django/contrib/contenttypes/models.py b/django/contrib/contenttypes/models.py
index 27a5388a70..8d459f65a2 100644
--- a/django/contrib/contenttypes/models.py
+++ b/django/contrib/contenttypes/models.py
@@ -13,13 +13,15 @@ from django.utils.translation import ugettext_lazy as _
class ContentTypeManager(models.Manager):
use_in_migrations = True
- # Cache to avoid re-looking up ContentType objects all over the place.
- # This cache is shared by all the get_for_* methods.
- _cache = {}
+ def __init__(self, *args, **kwargs):
+ super(ContentTypeManager, self).__init__(*args, **kwargs)
+ # Cache shared by all the get_for_* methods to speed up
+ # ContentType retrieval.
+ self._cache = {}
def get_by_natural_key(self, app_label, model):
try:
- ct = self.__class__._cache[self.db][(app_label, model)]
+ ct = self._cache[self.db][(app_label, model)]
except KeyError:
ct = self.get(app_label=app_label, model=model)
self._add_to_cache(self.db, ct)
@@ -34,7 +36,7 @@ class ContentTypeManager(models.Manager):
def _get_from_cache(self, opts):
key = (opts.app_label, opts.model_name)
- return self.__class__._cache[self.db][key]
+ return self._cache[self.db][key]
def create(self, **kwargs):
if 'name' in kwargs:
@@ -129,7 +131,7 @@ class ContentTypeManager(models.Manager):
(though ContentTypes are obviously not created on-the-fly by get_by_id).
"""
try:
- ct = self.__class__._cache[self.db][id]
+ ct = self._cache[self.db][id]
except KeyError:
# This could raise a DoesNotExist; that's correct behavior and will
# make sure that only correct ctypes get stored in the cache dict.
@@ -144,15 +146,15 @@ class ContentTypeManager(models.Manager):
django.contrib.contenttypes.management.update_contenttypes for where
this gets called).
"""
- self.__class__._cache.clear()
+ self._cache.clear()
def _add_to_cache(self, using, ct):
"""Insert a ContentType into the cache."""
# Note it's possible for ContentType objects to be stale; model_class() will return None.
# Hence, there is no reliance on model._meta.app_label here, just using the model fields instead.
key = (ct.app_label, ct.model)
- self.__class__._cache.setdefault(using, {})[key] = ct
- self.__class__._cache.setdefault(using, {})[ct.id] = ct
+ self._cache.setdefault(using, {})[key] = ct
+ self._cache.setdefault(using, {})[ct.id] = ct
@python_2_unicode_compatible
diff --git a/docs/releases/1.8.10.txt b/docs/releases/1.8.10.txt
index c9fbd0470d..b1319ff802 100644
--- a/docs/releases/1.8.10.txt
+++ b/docs/releases/1.8.10.txt
@@ -26,3 +26,6 @@ Bugfixes
``URLValidator`` to fix a regression in Django 1.8 (:ticket:`26204`).
* Fixed ``BoundField`` to reallow slices of subwidgets (:ticket:`26267`).
+
+* Prevented ``ContentTypeManager`` instances from sharing their cache
+ (:ticket:`26286`).
diff --git a/tests/contenttypes_tests/test_models.py b/tests/contenttypes_tests/test_models.py
index eb436b6a64..75cea0a4d8 100644
--- a/tests/contenttypes_tests/test_models.py
+++ b/tests/contenttypes_tests/test_models.py
@@ -2,7 +2,7 @@ from __future__ import unicode_literals
import warnings
-from django.contrib.contenttypes.models import ContentType
+from django.contrib.contenttypes.models import ContentType, ContentTypeManager
from django.contrib.contenttypes.views import shortcut
from django.contrib.sites.shortcuts import get_current_site
from django.db.utils import IntegrityError, OperationalError, ProgrammingError
@@ -170,6 +170,18 @@ class ContentTypesTests(TestCase):
DeferredProxyModel: proxy_model_ct,
})
+ def test_cache_not_shared_between_managers(self):
+ with self.assertNumQueries(1):
+ ContentType.objects.get_for_model(ContentType)
+ with self.assertNumQueries(0):
+ ContentType.objects.get_for_model(ContentType)
+ other_manager = ContentTypeManager()
+ other_manager.model = ContentType
+ with self.assertNumQueries(1):
+ other_manager.get_for_model(ContentType)
+ with self.assertNumQueries(0):
+ other_manager.get_for_model(ContentType)
+
@override_settings(ALLOWED_HOSTS=['example.com'])
def test_shortcut_view(self):
"""