tests.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470
  1. import datetime
  2. from django.contrib.auth.models import User
  3. from django.test import TestCase
  4. from .models import Order, RevisionableModel, TestObject
  5. class ExtraRegressTests(TestCase):
  6. @classmethod
  7. def setUpTestData(cls):
  8. cls.u = User.objects.create_user(
  9. username="fred", password="secret", email="fred@example.com"
  10. )
  11. def test_regression_7314_7372(self):
  12. """
  13. Regression tests for #7314 and #7372
  14. """
  15. rm = RevisionableModel.objects.create(
  16. title="First Revision", when=datetime.datetime(2008, 9, 28, 10, 30, 0)
  17. )
  18. self.assertEqual(rm.pk, rm.base.pk)
  19. rm2 = rm.new_revision()
  20. rm2.title = "Second Revision"
  21. rm.when = datetime.datetime(2008, 9, 28, 14, 25, 0)
  22. rm2.save()
  23. self.assertEqual(rm2.title, "Second Revision")
  24. self.assertEqual(rm2.base.title, "First Revision")
  25. self.assertNotEqual(rm2.pk, rm.pk)
  26. self.assertEqual(rm2.base.pk, rm.pk)
  27. # Queryset to match most recent revision:
  28. qs = RevisionableModel.objects.extra(
  29. where=[
  30. "%(table)s.id IN "
  31. "(SELECT MAX(rev.id) FROM %(table)s rev GROUP BY rev.base_id)"
  32. % {
  33. "table": RevisionableModel._meta.db_table,
  34. }
  35. ]
  36. )
  37. self.assertQuerySetEqual(
  38. qs,
  39. [("Second Revision", "First Revision")],
  40. transform=lambda r: (r.title, r.base.title),
  41. )
  42. # Queryset to search for string in title:
  43. qs2 = RevisionableModel.objects.filter(title__contains="Revision")
  44. self.assertQuerySetEqual(
  45. qs2,
  46. [
  47. ("First Revision", "First Revision"),
  48. ("Second Revision", "First Revision"),
  49. ],
  50. transform=lambda r: (r.title, r.base.title),
  51. ordered=False,
  52. )
  53. # Following queryset should return the most recent revision:
  54. self.assertQuerySetEqual(
  55. qs & qs2,
  56. [("Second Revision", "First Revision")],
  57. transform=lambda r: (r.title, r.base.title),
  58. ordered=False,
  59. )
  60. def test_extra_stay_tied(self):
  61. # Extra select parameters should stay tied to their corresponding
  62. # select portions. Applies when portions are updated or otherwise
  63. # moved around.
  64. qs = User.objects.extra(
  65. select={"alpha": "%s", "beta": "2", "gamma": "%s"}, select_params=(1, 3)
  66. )
  67. qs = qs.extra(select={"beta": 4})
  68. qs = qs.extra(select={"alpha": "%s"}, select_params=[5])
  69. self.assertEqual(
  70. list(qs.filter(id=self.u.id).values("alpha", "beta", "gamma")),
  71. [{"alpha": 5, "beta": 4, "gamma": 3}],
  72. )
  73. def test_regression_7957(self):
  74. """
  75. Regression test for #7957: Combining extra() calls should leave the
  76. corresponding parameters associated with the right extra() bit. I.e.
  77. internal dictionary must remain sorted.
  78. """
  79. self.assertEqual(
  80. (
  81. User.objects.extra(select={"alpha": "%s"}, select_params=(1,))
  82. .extra(select={"beta": "%s"}, select_params=(2,))[0]
  83. .alpha
  84. ),
  85. 1,
  86. )
  87. self.assertEqual(
  88. (
  89. User.objects.extra(select={"beta": "%s"}, select_params=(1,))
  90. .extra(select={"alpha": "%s"}, select_params=(2,))[0]
  91. .alpha
  92. ),
  93. 2,
  94. )
  95. def test_regression_7961(self):
  96. """
  97. Regression test for #7961: When not using a portion of an
  98. extra(...) in a query, remove any corresponding parameters from the
  99. query as well.
  100. """
  101. self.assertEqual(
  102. list(
  103. User.objects.extra(select={"alpha": "%s"}, select_params=(-6,))
  104. .filter(id=self.u.id)
  105. .values_list("id", flat=True)
  106. ),
  107. [self.u.id],
  108. )
  109. def test_regression_8063(self):
  110. """
  111. Regression test for #8063: limiting a query shouldn't discard any
  112. extra() bits.
  113. """
  114. qs = User.objects.extra(where=["id=%s"], params=[self.u.id])
  115. self.assertSequenceEqual(qs, [self.u])
  116. self.assertSequenceEqual(qs[:1], [self.u])
  117. def test_regression_8039(self):
  118. """
  119. Regression test for #8039: Ordering sometimes removed relevant tables
  120. from extra(). This test is the critical case: ordering uses a table,
  121. but then removes the reference because of an optimization. The table
  122. should still be present because of the extra() call.
  123. """
  124. self.assertQuerySetEqual(
  125. (
  126. Order.objects.extra(
  127. where=["username=%s"], params=["fred"], tables=["auth_user"]
  128. ).order_by("created_by")
  129. ),
  130. [],
  131. )
  132. def test_regression_8819(self):
  133. """
  134. Regression test for #8819: Fields in the extra(select=...) list
  135. should be available to extra(order_by=...).
  136. """
  137. self.assertSequenceEqual(
  138. User.objects.filter(pk=self.u.id)
  139. .extra(select={"extra_field": 1})
  140. .distinct(),
  141. [self.u],
  142. )
  143. self.assertSequenceEqual(
  144. User.objects.filter(pk=self.u.id).extra(
  145. select={"extra_field": 1}, order_by=["extra_field"]
  146. ),
  147. [self.u],
  148. )
  149. self.assertSequenceEqual(
  150. User.objects.filter(pk=self.u.id)
  151. .extra(select={"extra_field": 1}, order_by=["extra_field"])
  152. .distinct(),
  153. [self.u],
  154. )
  155. def test_dates_query(self):
  156. """
  157. When calling the dates() method on a queryset with extra selection
  158. columns, we can (and should) ignore those columns. They don't change
  159. the result and cause incorrect SQL to be produced otherwise.
  160. """
  161. RevisionableModel.objects.create(
  162. title="First Revision", when=datetime.datetime(2008, 9, 28, 10, 30, 0)
  163. )
  164. self.assertSequenceEqual(
  165. RevisionableModel.objects.extra(select={"the_answer": "id"}).datetimes(
  166. "when", "month"
  167. ),
  168. [datetime.datetime(2008, 9, 1, 0, 0)],
  169. )
  170. def test_values_with_extra(self):
  171. """
  172. Regression test for #10256... If there is a values() clause, Extra
  173. columns are only returned if they are explicitly mentioned.
  174. """
  175. obj = TestObject(first="first", second="second", third="third")
  176. obj.save()
  177. self.assertEqual(
  178. list(
  179. TestObject.objects.extra(
  180. select={"foo": "first", "bar": "second", "whiz": "third"}
  181. ).values()
  182. ),
  183. [
  184. {
  185. "bar": "second",
  186. "third": "third",
  187. "second": "second",
  188. "whiz": "third",
  189. "foo": "first",
  190. "id": obj.pk,
  191. "first": "first",
  192. }
  193. ],
  194. )
  195. # Extra clauses after an empty values clause are still included
  196. self.assertEqual(
  197. list(
  198. TestObject.objects.values().extra(
  199. select={"foo": "first", "bar": "second", "whiz": "third"}
  200. )
  201. ),
  202. [
  203. {
  204. "bar": "second",
  205. "third": "third",
  206. "second": "second",
  207. "whiz": "third",
  208. "foo": "first",
  209. "id": obj.pk,
  210. "first": "first",
  211. }
  212. ],
  213. )
  214. # Extra columns are ignored if not mentioned in the values() clause
  215. self.assertEqual(
  216. list(
  217. TestObject.objects.extra(
  218. select={"foo": "first", "bar": "second", "whiz": "third"}
  219. ).values("first", "second")
  220. ),
  221. [{"second": "second", "first": "first"}],
  222. )
  223. # Extra columns after a non-empty values() clause are ignored
  224. self.assertEqual(
  225. list(
  226. TestObject.objects.values("first", "second").extra(
  227. select={"foo": "first", "bar": "second", "whiz": "third"}
  228. )
  229. ),
  230. [{"second": "second", "first": "first"}],
  231. )
  232. # Extra columns can be partially returned
  233. self.assertEqual(
  234. list(
  235. TestObject.objects.extra(
  236. select={"foo": "first", "bar": "second", "whiz": "third"}
  237. ).values("first", "second", "foo")
  238. ),
  239. [{"second": "second", "foo": "first", "first": "first"}],
  240. )
  241. # Also works if only extra columns are included
  242. self.assertEqual(
  243. list(
  244. TestObject.objects.extra(
  245. select={"foo": "first", "bar": "second", "whiz": "third"}
  246. ).values("foo", "whiz")
  247. ),
  248. [{"foo": "first", "whiz": "third"}],
  249. )
  250. # Values list works the same way
  251. # All columns are returned for an empty values_list()
  252. self.assertEqual(
  253. list(
  254. TestObject.objects.extra(
  255. select={"foo": "first", "bar": "second", "whiz": "third"}
  256. ).values_list()
  257. ),
  258. [("first", "second", "third", obj.pk, "first", "second", "third")],
  259. )
  260. # Extra columns after an empty values_list() are still included
  261. self.assertEqual(
  262. list(
  263. TestObject.objects.values_list().extra(
  264. select={"foo": "first", "bar": "second", "whiz": "third"}
  265. )
  266. ),
  267. [("first", "second", "third", obj.pk, "first", "second", "third")],
  268. )
  269. # Extra columns ignored completely if not mentioned in values_list()
  270. self.assertEqual(
  271. list(
  272. TestObject.objects.extra(
  273. select={"foo": "first", "bar": "second", "whiz": "third"}
  274. ).values_list("first", "second")
  275. ),
  276. [("first", "second")],
  277. )
  278. # Extra columns after a non-empty values_list() clause are ignored completely
  279. self.assertEqual(
  280. list(
  281. TestObject.objects.values_list("first", "second").extra(
  282. select={"foo": "first", "bar": "second", "whiz": "third"}
  283. )
  284. ),
  285. [("first", "second")],
  286. )
  287. self.assertEqual(
  288. list(
  289. TestObject.objects.extra(
  290. select={"foo": "first", "bar": "second", "whiz": "third"}
  291. ).values_list("second", flat=True)
  292. ),
  293. ["second"],
  294. )
  295. # Only the extra columns specified in the values_list() are returned
  296. self.assertEqual(
  297. list(
  298. TestObject.objects.extra(
  299. select={"foo": "first", "bar": "second", "whiz": "third"}
  300. ).values_list("first", "second", "whiz")
  301. ),
  302. [("first", "second", "third")],
  303. )
  304. # ...also works if only extra columns are included
  305. self.assertEqual(
  306. list(
  307. TestObject.objects.extra(
  308. select={"foo": "first", "bar": "second", "whiz": "third"}
  309. ).values_list("foo", "whiz")
  310. ),
  311. [("first", "third")],
  312. )
  313. self.assertEqual(
  314. list(
  315. TestObject.objects.extra(
  316. select={"foo": "first", "bar": "second", "whiz": "third"}
  317. ).values_list("whiz", flat=True)
  318. ),
  319. ["third"],
  320. )
  321. # ... and values are returned in the order they are specified
  322. self.assertEqual(
  323. list(
  324. TestObject.objects.extra(
  325. select={"foo": "first", "bar": "second", "whiz": "third"}
  326. ).values_list("whiz", "foo")
  327. ),
  328. [("third", "first")],
  329. )
  330. self.assertEqual(
  331. list(
  332. TestObject.objects.extra(
  333. select={"foo": "first", "bar": "second", "whiz": "third"}
  334. ).values_list("first", "id")
  335. ),
  336. [("first", obj.pk)],
  337. )
  338. self.assertEqual(
  339. list(
  340. TestObject.objects.extra(
  341. select={"foo": "first", "bar": "second", "whiz": "third"}
  342. ).values_list("whiz", "first", "bar", "id")
  343. ),
  344. [("third", "first", "second", obj.pk)],
  345. )
  346. def test_regression_10847(self):
  347. """
  348. Regression for #10847: the list of extra columns can always be
  349. accurately evaluated. Using an inner query ensures that as_sql() is
  350. producing correct output without requiring full evaluation and
  351. execution of the inner query.
  352. """
  353. obj = TestObject(first="first", second="second", third="third")
  354. obj.save()
  355. self.assertEqual(
  356. list(TestObject.objects.extra(select={"extra": 1}).values("pk")),
  357. [{"pk": obj.pk}],
  358. )
  359. self.assertSequenceEqual(
  360. TestObject.objects.filter(
  361. pk__in=TestObject.objects.extra(select={"extra": 1}).values("pk")
  362. ),
  363. [obj],
  364. )
  365. self.assertEqual(
  366. list(TestObject.objects.values("pk").extra(select={"extra": 1})),
  367. [{"pk": obj.pk}],
  368. )
  369. self.assertSequenceEqual(
  370. TestObject.objects.filter(
  371. pk__in=TestObject.objects.values("pk").extra(select={"extra": 1})
  372. ),
  373. [obj],
  374. )
  375. self.assertSequenceEqual(
  376. TestObject.objects.filter(pk=obj.pk)
  377. | TestObject.objects.extra(where=["id > %s"], params=[obj.pk]),
  378. [obj],
  379. )
  380. def test_regression_17877(self):
  381. """
  382. Extra WHERE clauses get correctly ANDed, even when they
  383. contain OR operations.
  384. """
  385. # Test Case 1: should appear in queryset.
  386. t1 = TestObject.objects.create(first="a", second="a", third="a")
  387. # Test Case 2: should appear in queryset.
  388. t2 = TestObject.objects.create(first="b", second="a", third="a")
  389. # Test Case 3: should not appear in queryset, bug case.
  390. t = TestObject(first="a", second="a", third="b")
  391. t.save()
  392. # Test Case 4: should not appear in queryset.
  393. t = TestObject(first="b", second="a", third="b")
  394. t.save()
  395. # Test Case 5: should not appear in queryset.
  396. t = TestObject(first="b", second="b", third="a")
  397. t.save()
  398. # Test Case 6: should not appear in queryset, bug case.
  399. t = TestObject(first="a", second="b", third="b")
  400. t.save()
  401. self.assertCountEqual(
  402. TestObject.objects.extra(
  403. where=["first = 'a' OR second = 'a'", "third = 'a'"],
  404. ),
  405. [t1, t2],
  406. )
  407. def test_extra_values_distinct_ordering(self):
  408. t1 = TestObject.objects.create(first="a", second="a", third="a")
  409. t2 = TestObject.objects.create(first="a", second="b", third="b")
  410. qs = (
  411. TestObject.objects.extra(select={"second_extra": "second"})
  412. .values_list("id", flat=True)
  413. .distinct()
  414. )
  415. self.assertSequenceEqual(qs.order_by("second_extra"), [t1.pk, t2.pk])
  416. self.assertSequenceEqual(qs.order_by("-second_extra"), [t2.pk, t1.pk])
  417. # Note: the extra ordering must appear in select clause, so we get two
  418. # non-distinct results here (this is on purpose, see #7070).
  419. # Extra select doesn't appear in result values.
  420. self.assertSequenceEqual(
  421. qs.order_by("-second_extra").values_list("first"), [("a",), ("a",)]
  422. )