From 92824e71028d40009d32fc68d459b0c2242fb7fe Mon Sep 17 00:00:00 2001 From: Malcolm Tredinnick Date: Sat, 11 Apr 2009 13:22:32 +0000 Subject: [PATCH] Fixed #10738 -- Fixed content type values for deferred and proxy models. Models with deferred fields, or which are proxying for an existing model, now return the same ContentType object as the real model they reflect. Thanks to tomasz.elendt for help with fixing this. git-svn-id: http://code.djangoproject.com/svn/django/trunk@10523 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/contrib/contenttypes/models.py | 19 +++++++++++-------- tests/modeltests/proxy_models/models.py | 7 +++++++ tests/regressiontests/defer_regress/models.py | 10 ++++++++++ 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/django/contrib/contenttypes/models.py b/django/contrib/contenttypes/models.py index 4df1edcc75..def7ce6986 100644 --- a/django/contrib/contenttypes/models.py +++ b/django/contrib/contenttypes/models.py @@ -7,7 +7,7 @@ class ContentTypeManager(models.Manager): # Cache to avoid re-looking up ContentType objects all over the place. # This cache is shared by all the get_for_* methods. _cache = {} - + def get_for_model(self, model): """ Returns the ContentType object for a given model, creating the @@ -15,22 +15,25 @@ class ContentTypeManager(models.Manager): for the same model don't hit the database. """ opts = model._meta + while opts.proxy: + model = opts.proxy_for_model + opts = model._meta key = (opts.app_label, opts.object_name.lower()) try: ct = self.__class__._cache[key] except KeyError: - # Load or create the ContentType entry. The smart_unicode() is + # Load or create the ContentType entry. The smart_unicode() is # needed around opts.verbose_name_raw because name_raw might be a # django.utils.functional.__proxy__ object. ct, created = self.get_or_create( app_label = opts.app_label, - model = opts.object_name.lower(), + model = opts.object_name.lower(), defaults = {'name': smart_unicode(opts.verbose_name_raw)}, ) self._add_to_cache(ct) - + return ct - + def get_for_id(self, id): """ Lookup a ContentType by ID. Uses the same shared cache as get_for_model @@ -44,7 +47,7 @@ class ContentTypeManager(models.Manager): ct = self.get(pk=id) self._add_to_cache(ct) return ct - + def clear_cache(self): """ Clear out the content-type cache. This needs to happen during database @@ -53,7 +56,7 @@ class ContentTypeManager(models.Manager): this gets called). """ self.__class__._cache.clear() - + def _add_to_cache(self, ct): """Insert a ContentType into the cache.""" model = ct.model_class() @@ -66,7 +69,7 @@ class ContentType(models.Model): app_label = models.CharField(max_length=100) model = models.CharField(_('python model class name'), max_length=100) objects = ContentTypeManager() - + class Meta: verbose_name = _('content type') verbose_name_plural = _('content types') diff --git a/tests/modeltests/proxy_models/models.py b/tests/modeltests/proxy_models/models.py index 275b5207cc..ab381129cf 100644 --- a/tests/modeltests/proxy_models/models.py +++ b/tests/modeltests/proxy_models/models.py @@ -5,6 +5,7 @@ than using a new table of their own. This allows them to act as simple proxies, providing a modified interface to the data from the base class. """ +from django.contrib.contenttypes.models import ContentType from django.db import models @@ -171,6 +172,12 @@ FieldError: Proxy model 'NoNewFields' contains model fields. [, ] >>> OtherPerson._default_manager.all() [, ] + +# A proxy has the same content type as the model it is proxying for (at the +# storage level, it is meant to be essentially indistinguishable). +>>> ctype = ContentType.objects.get_for_model +>>> ctype(Person) is ctype(OtherPerson) +True """} diff --git a/tests/regressiontests/defer_regress/models.py b/tests/regressiontests/defer_regress/models.py index e2a5944f92..11ce1557fe 100644 --- a/tests/regressiontests/defer_regress/models.py +++ b/tests/regressiontests/defer_regress/models.py @@ -3,6 +3,7 @@ Regression tests for defer() / only() behavior. """ from django.conf import settings +from django.contrib.contenttypes.models import ContentType from django.db import connection, models class Item(models.Model): @@ -91,5 +92,14 @@ u'c1' >>> Leaf.objects.select_related().only("child__name", "second_child__name") [] +Models instances with deferred fields should still return the same content +types as their non-deferred versions (bug #10738). +>>> ctype = ContentType.objects.get_for_model +>>> c1 = ctype(Item.objects.all()[0]) +>>> c2 = ctype(Item.objects.defer("name")[0]) +>>> c3 = ctype(Item.objects.only("name")[0]) +>>> c1 is c2 is c3 +True + """ }