summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnssi Kääriäinen <akaariai@gmail.com>2014-07-14 17:53:23 +0300
committerAnssi Kääriäinen <akaariai@gmail.com>2014-08-12 14:58:10 +0300
commitf7dba61881ff5c26ad1c9a6dff71a8dc4ebede5a (patch)
treed5fca27a8844f00fa7c133c2a88d0428847460b3
parent5b0375ec3e7473fcc29c21003fef80c0d0be183f (diff)
Avoided creation of deferred model from another deferred model
Also never create deferred model when no attrs are deferred.
-rw-r--r--django/db/models/query_utils.py8
-rw-r--r--tests/defer_regress/tests.py13
2 files changed, 20 insertions, 1 deletions
diff --git a/django/db/models/query_utils.py b/django/db/models/query_utils.py
index be37bc8632..a8699f6334 100644
--- a/django/db/models/query_utils.py
+++ b/django/db/models/query_utils.py
@@ -186,6 +186,14 @@ def deferred_class_factory(model, attrs):
being replaced with DeferredAttribute objects. The "pk_value" ties the
deferred attributes to a particular instance of the model.
"""
+ if not attrs:
+ return model
+ # Never create deferred models based on deferred model
+ if model._deferred:
+ # Deferred models are proxies for the non-deferred model. We never
+ # create chains of defers => proxy_for_model is the non-deferred
+ # model.
+ model = model._meta.proxy_for_model
# The app registry wants a unique name for each model, otherwise the new
# class won't be created (we get an exception). Therefore, we generate
# the name using the passed in attrs. It's OK to reuse an existing class
diff --git a/tests/defer_regress/tests.py b/tests/defer_regress/tests.py
index 63f9e97258..f2a015a7a1 100644
--- a/tests/defer_regress/tests.py
+++ b/tests/defer_regress/tests.py
@@ -6,6 +6,7 @@ from django.apps import apps
from django.contrib.contenttypes.models import ContentType
from django.contrib.sessions.backends.db import SessionStore
from django.db.models import Count
+from django.db.models.query_utils import deferred_class_factory, DeferredAttribute
from django.test import TestCase, override_settings
from .models import (
@@ -233,7 +234,6 @@ class DeferRegressionTest(TestCase):
self.assertEqual(len(qs), 1)
def test_deferred_class_factory(self):
- from django.db.models.query_utils import deferred_class_factory
new_class = deferred_class_factory(
Item,
('this_is_some_very_long_attribute_name_so_modelname_truncation_is_triggered',))
@@ -241,6 +241,17 @@ class DeferRegressionTest(TestCase):
new_class.__name__,
'Item_Deferred_this_is_some_very_long_attribute_nac34b1f495507dad6b02e2cb235c875e')
+ def test_deferred_class_factory_already_deferred(self):
+ deferred_item1 = deferred_class_factory(Item, ('name',))
+ deferred_item2 = deferred_class_factory(deferred_item1, ('value',))
+ self.assertIs(deferred_item2._meta.proxy_for_model, Item)
+ self.assertFalse(isinstance(deferred_item2.__dict__.get('name'), DeferredAttribute))
+ self.assertTrue(isinstance(deferred_item2.__dict__.get('value'), DeferredAttribute))
+
+ def test_deferred_class_factory_no_attrs(self):
+ deferred_cls = deferred_class_factory(Item, ())
+ self.assertFalse(deferred_cls._deferred)
+
class DeferAnnotateSelectRelatedTest(TestCase):
def test_defer_annotate_select_related(self):