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 + """ }