tests.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. from __future__ import unicode_literals
  2. from operator import attrgetter
  3. from django.apps import apps
  4. from django.contrib.contenttypes.models import ContentType
  5. from django.contrib.sessions.backends.db import SessionStore
  6. from django.db.models import Count
  7. from django.db.models.query_utils import deferred_class_factory, DeferredAttribute
  8. from django.test import TestCase, override_settings
  9. from .models import (
  10. ResolveThis, Item, RelatedItem, Child, Leaf, Proxy, SimpleItem, Feature,
  11. ItemAndSimpleItem, OneToOneItem, SpecialFeature, Location, Request,
  12. ProxyRelated, Derived, Base,
  13. )
  14. class DeferRegressionTest(TestCase):
  15. def test_basic(self):
  16. # Deferred fields should really be deferred and not accidentally use
  17. # the field's default value just because they aren't passed to __init__
  18. Item.objects.create(name="first", value=42)
  19. obj = Item.objects.only("name", "other_value").get(name="first")
  20. # Accessing "name" doesn't trigger a new database query. Accessing
  21. # "value" or "text" should.
  22. with self.assertNumQueries(0):
  23. self.assertEqual(obj.name, "first")
  24. self.assertEqual(obj.other_value, 0)
  25. with self.assertNumQueries(1):
  26. self.assertEqual(obj.value, 42)
  27. with self.assertNumQueries(1):
  28. self.assertEqual(obj.text, "xyzzy")
  29. with self.assertNumQueries(0):
  30. self.assertEqual(obj.text, "xyzzy")
  31. # Regression test for #10695. Make sure different instances don't
  32. # inadvertently share data in the deferred descriptor objects.
  33. i = Item.objects.create(name="no I'm first", value=37)
  34. items = Item.objects.only("value").order_by("-value")
  35. self.assertEqual(items[0].name, "first")
  36. self.assertEqual(items[1].name, "no I'm first")
  37. RelatedItem.objects.create(item=i)
  38. r = RelatedItem.objects.defer("item").get()
  39. self.assertEqual(r.item_id, i.id)
  40. self.assertEqual(r.item, i)
  41. # Some further checks for select_related() and inherited model
  42. # behavior (regression for #10710).
  43. c1 = Child.objects.create(name="c1", value=42)
  44. c2 = Child.objects.create(name="c2", value=37)
  45. Leaf.objects.create(name="l1", child=c1, second_child=c2)
  46. obj = Leaf.objects.only("name", "child").select_related()[0]
  47. self.assertEqual(obj.child.name, "c1")
  48. self.assertQuerysetEqual(
  49. Leaf.objects.select_related().only("child__name", "second_child__name"), [
  50. "l1",
  51. ],
  52. attrgetter("name")
  53. )
  54. # Models instances with deferred fields should still return the same
  55. # content types as their non-deferred versions (bug #10738).
  56. ctype = ContentType.objects.get_for_model
  57. c1 = ctype(Item.objects.all()[0])
  58. c2 = ctype(Item.objects.defer("name")[0])
  59. c3 = ctype(Item.objects.only("name")[0])
  60. self.assertTrue(c1 is c2 is c3)
  61. # Regression for #10733 - only() can be used on a model with two
  62. # foreign keys.
  63. results = Leaf.objects.only("name", "child", "second_child").select_related()
  64. self.assertEqual(results[0].child.name, "c1")
  65. self.assertEqual(results[0].second_child.name, "c2")
  66. results = Leaf.objects.only(
  67. "name", "child", "second_child", "child__name", "second_child__name"
  68. ).select_related()
  69. self.assertEqual(results[0].child.name, "c1")
  70. self.assertEqual(results[0].second_child.name, "c2")
  71. # Regression for #16409 - make sure defer() and only() work with annotate()
  72. self.assertIsInstance(
  73. list(SimpleItem.objects.annotate(Count('feature')).defer('name')),
  74. list)
  75. self.assertIsInstance(
  76. list(SimpleItem.objects.annotate(Count('feature')).only('name')),
  77. list)
  78. def test_ticket_11936(self):
  79. app_config = apps.get_app_config("defer_regress")
  80. # Regression for #11936 - get_models should not return deferred models
  81. # by default. Run a couple of defer queries so that app registry must
  82. # contain some deferred classes. It might contain a lot more classes
  83. # depending on the order the tests are ran.
  84. list(Item.objects.defer("name"))
  85. list(Child.objects.defer("value"))
  86. klasses = {model.__name__ for model in app_config.get_models()}
  87. self.assertIn("Child", klasses)
  88. self.assertIn("Item", klasses)
  89. self.assertNotIn("Child_Deferred_value", klasses)
  90. self.assertNotIn("Item_Deferred_name", klasses)
  91. self.assertFalse(any(k._deferred for k in app_config.get_models()))
  92. klasses_with_deferred = {model.__name__ for model in app_config.get_models(include_deferred=True)}
  93. self.assertIn("Child", klasses_with_deferred)
  94. self.assertIn("Item", klasses_with_deferred)
  95. self.assertIn("Child_Deferred_value", klasses_with_deferred)
  96. self.assertIn("Item_Deferred_name", klasses_with_deferred)
  97. self.assertTrue(any(k._deferred for k in app_config.get_models(include_deferred=True)))
  98. @override_settings(SESSION_SERIALIZER='django.contrib.sessions.serializers.PickleSerializer')
  99. def test_ticket_12163(self):
  100. # Test for #12163 - Pickling error saving session with unsaved model
  101. # instances.
  102. SESSION_KEY = '2b1189a188b44ad18c35e1baac6ceead'
  103. item = Item()
  104. item._deferred = False
  105. s = SessionStore(SESSION_KEY)
  106. s.clear()
  107. s["item"] = item
  108. s.save()
  109. s = SessionStore(SESSION_KEY)
  110. s.modified = True
  111. s.save()
  112. i2 = s["item"]
  113. self.assertFalse(i2._deferred)
  114. def test_ticket_16409(self):
  115. # Regression for #16409 - make sure defer() and only() work with annotate()
  116. self.assertIsInstance(
  117. list(SimpleItem.objects.annotate(Count('feature')).defer('name')),
  118. list)
  119. self.assertIsInstance(
  120. list(SimpleItem.objects.annotate(Count('feature')).only('name')),
  121. list)
  122. def test_ticket_23270(self):
  123. Derived.objects.create(text="foo", other_text="bar")
  124. with self.assertNumQueries(1):
  125. obj = Base.objects.select_related("derived").defer("text")[0]
  126. self.assertIsInstance(obj.derived, Derived)
  127. self.assertEqual("bar", obj.derived.other_text)
  128. self.assertNotIn("text", obj.__dict__)
  129. self.assertEqual(1, obj.derived.base_ptr_id)
  130. def test_only_and_defer_usage_on_proxy_models(self):
  131. # Regression for #15790 - only() broken for proxy models
  132. proxy = Proxy.objects.create(name="proxy", value=42)
  133. msg = 'QuerySet.only() return bogus results with proxy models'
  134. dp = Proxy.objects.only('other_value').get(pk=proxy.pk)
  135. self.assertEqual(dp.name, proxy.name, msg=msg)
  136. self.assertEqual(dp.value, proxy.value, msg=msg)
  137. # also test things with .defer()
  138. msg = 'QuerySet.defer() return bogus results with proxy models'
  139. dp = Proxy.objects.defer('name', 'text', 'value').get(pk=proxy.pk)
  140. self.assertEqual(dp.name, proxy.name, msg=msg)
  141. self.assertEqual(dp.value, proxy.value, msg=msg)
  142. def test_resolve_columns(self):
  143. ResolveThis.objects.create(num=5.0, name='Foobar')
  144. qs = ResolveThis.objects.defer('num')
  145. self.assertEqual(1, qs.count())
  146. self.assertEqual('Foobar', qs[0].name)
  147. def test_reverse_one_to_one_relations(self):
  148. # Refs #14694. Test reverse relations which are known unique (reverse
  149. # side has o2ofield or unique FK) - the o2o case
  150. item = Item.objects.create(name="first", value=42)
  151. o2o = OneToOneItem.objects.create(item=item, name="second")
  152. self.assertEqual(len(Item.objects.defer('one_to_one_item__name')), 1)
  153. self.assertEqual(len(Item.objects.select_related('one_to_one_item')), 1)
  154. self.assertEqual(len(Item.objects.select_related(
  155. 'one_to_one_item').defer('one_to_one_item__name')), 1)
  156. self.assertEqual(len(Item.objects.select_related('one_to_one_item').defer('value')), 1)
  157. # Make sure that `only()` doesn't break when we pass in a unique relation,
  158. # rather than a field on the relation.
  159. self.assertEqual(len(Item.objects.only('one_to_one_item')), 1)
  160. with self.assertNumQueries(1):
  161. i = Item.objects.select_related('one_to_one_item')[0]
  162. self.assertEqual(i.one_to_one_item.pk, o2o.pk)
  163. self.assertEqual(i.one_to_one_item.name, "second")
  164. with self.assertNumQueries(1):
  165. i = Item.objects.select_related('one_to_one_item').defer(
  166. 'value', 'one_to_one_item__name')[0]
  167. self.assertEqual(i.one_to_one_item.pk, o2o.pk)
  168. self.assertEqual(i.name, "first")
  169. with self.assertNumQueries(1):
  170. self.assertEqual(i.one_to_one_item.name, "second")
  171. with self.assertNumQueries(1):
  172. self.assertEqual(i.value, 42)
  173. def test_defer_with_select_related(self):
  174. item1 = Item.objects.create(name="first", value=47)
  175. item2 = Item.objects.create(name="second", value=42)
  176. simple = SimpleItem.objects.create(name="simple", value="23")
  177. ItemAndSimpleItem.objects.create(item=item1, simple=simple)
  178. obj = ItemAndSimpleItem.objects.defer('item').select_related('simple').get()
  179. self.assertEqual(obj.item, item1)
  180. self.assertEqual(obj.item_id, item1.id)
  181. obj.item = item2
  182. obj.save()
  183. obj = ItemAndSimpleItem.objects.defer('item').select_related('simple').get()
  184. self.assertEqual(obj.item, item2)
  185. self.assertEqual(obj.item_id, item2.id)
  186. def test_proxy_model_defer_with_selected_related(self):
  187. # Regression for #22050
  188. item = Item.objects.create(name="first", value=47)
  189. RelatedItem.objects.create(item=item)
  190. # Defer fields with only()
  191. obj = ProxyRelated.objects.all().select_related().only('item__name')[0]
  192. with self.assertNumQueries(0):
  193. self.assertEqual(obj.item.name, "first")
  194. with self.assertNumQueries(1):
  195. self.assertEqual(obj.item.value, 47)
  196. def test_only_with_select_related(self):
  197. # Test for #17485.
  198. item = SimpleItem.objects.create(name='first', value=47)
  199. feature = Feature.objects.create(item=item)
  200. SpecialFeature.objects.create(feature=feature)
  201. qs = Feature.objects.only('item__name').select_related('item')
  202. self.assertEqual(len(qs), 1)
  203. qs = SpecialFeature.objects.only('feature__item__name').select_related('feature__item')
  204. self.assertEqual(len(qs), 1)
  205. def test_deferred_class_factory(self):
  206. new_class = deferred_class_factory(
  207. Item,
  208. ('this_is_some_very_long_attribute_name_so_modelname_truncation_is_triggered',))
  209. self.assertEqual(
  210. new_class.__name__,
  211. 'Item_Deferred_this_is_some_very_long_attribute_nac34b1f495507dad6b02e2cb235c875e')
  212. def test_deferred_class_factory_already_deferred(self):
  213. deferred_item1 = deferred_class_factory(Item, ('name',))
  214. deferred_item2 = deferred_class_factory(deferred_item1, ('value',))
  215. self.assertIs(deferred_item2._meta.proxy_for_model, Item)
  216. self.assertFalse(isinstance(deferred_item2.__dict__.get('name'), DeferredAttribute))
  217. self.assertTrue(isinstance(deferred_item2.__dict__.get('value'), DeferredAttribute))
  218. def test_deferred_class_factory_no_attrs(self):
  219. deferred_cls = deferred_class_factory(Item, ())
  220. self.assertFalse(deferred_cls._deferred)
  221. class DeferAnnotateSelectRelatedTest(TestCase):
  222. def test_defer_annotate_select_related(self):
  223. location = Location.objects.create()
  224. Request.objects.create(location=location)
  225. self.assertIsInstance(list(Request.objects
  226. .annotate(Count('items')).select_related('profile', 'location')
  227. .only('profile', 'location')), list)
  228. self.assertIsInstance(list(Request.objects
  229. .annotate(Count('items')).select_related('profile', 'location')
  230. .only('profile__profile1', 'location__location1')), list)
  231. self.assertIsInstance(list(Request.objects
  232. .annotate(Count('items')).select_related('profile', 'location')
  233. .defer('request1', 'request2', 'request3', 'request4')), list)