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 năm trước cách đây
mục cha
commit
f7dba61881
2 tập tin đã thay đổi với 20 bổ sung1 xóa
  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):