From 86e761fee88a8bf0499f6795c9a9b336d7c52873 Mon Sep 17 00:00:00 2001 From: Diederik van der Boor Date: Sat, 18 May 2013 17:27:12 +0200 Subject: [PATCH] Fix NoneType error when fetching a stale ContentType with get_for_id When a stale ContentType is fetched, the _add_to_cache() function didn't detect that `model_class()` returns `None` (which it does by design). However, the `app_label` + `model` fields can be used instead to as local cache key. Third party apps can detect stale models by checking whether `model_class()` returns `None`. Ticket: https://code.djangoproject.com/ticket/20442 --- django/contrib/contenttypes/models.py | 6 ++++-- django/contrib/contenttypes/tests.py | 7 +++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/django/contrib/contenttypes/models.py b/django/contrib/contenttypes/models.py index f0bd109b00..ce220c8487 100644 --- a/django/contrib/contenttypes/models.py +++ b/django/contrib/contenttypes/models.py @@ -118,11 +118,13 @@ class ContentTypeManager(models.Manager): def _add_to_cache(self, using, ct): """Insert a ContentType into the cache.""" - model = ct.model_class() - key = (model._meta.app_label, model._meta.model_name) + # 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 + @python_2_unicode_compatible class ContentType(models.Model): name = models.CharField(max_length=100) diff --git a/django/contrib/contenttypes/tests.py b/django/contrib/contenttypes/tests.py index 7937873a00..f300294cd6 100644 --- a/django/contrib/contenttypes/tests.py +++ b/django/contrib/contenttypes/tests.py @@ -274,3 +274,10 @@ class ContentTypesTests(TestCase): model = 'OldModel', ) self.assertEqual(six.text_type(ct), 'Old model') + self.assertIsNone(ct.model_class()) + + # Make sure stale ContentTypes can be fetched like any other object. + # Before Django 1.6 this caused a NoneType error in the caching mechanism. + # Instead, just return the ContentType object and let the app detect stale states. + ct_fetched = ContentType.objects.get_for_id(ct.pk) + self.assertIsNone(ct_fetched.model_class())