Browse Source

Avoided creation of deferred model from another deferred model

Also never create deferred model when no attrs are deferred.
Anssi Kääriäinen 10 years ago
parent
commit
f7dba61881
2 changed files with 20 additions and 1 deletions
  1. 8 0
      django/db/models/query_utils.py
  2. 12 1
      tests/defer_regress/tests.py

+ 8 - 0
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

+ 12 - 1
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):