tests.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. from datetime import datetime
  2. from operator import attrgetter
  3. from django.db.models import F
  4. from django.db.models.functions import Upper
  5. from django.test import TestCase
  6. from .models import Article, Author, Reference
  7. class OrderingTests(TestCase):
  8. @classmethod
  9. def setUpTestData(cls):
  10. cls.a1 = Article.objects.create(headline="Article 1", pub_date=datetime(2005, 7, 26))
  11. cls.a2 = Article.objects.create(headline="Article 2", pub_date=datetime(2005, 7, 27))
  12. cls.a3 = Article.objects.create(headline="Article 3", pub_date=datetime(2005, 7, 27))
  13. cls.a4 = Article.objects.create(headline="Article 4", pub_date=datetime(2005, 7, 28))
  14. cls.author_1 = Author.objects.create(name="Name 1")
  15. cls.author_2 = Author.objects.create(name="Name 2")
  16. for i in range(2):
  17. Author.objects.create()
  18. def test_default_ordering(self):
  19. """
  20. By default, Article.objects.all() orders by pub_date descending, then
  21. headline ascending.
  22. """
  23. self.assertQuerysetEqual(
  24. Article.objects.all(), [
  25. "Article 4",
  26. "Article 2",
  27. "Article 3",
  28. "Article 1",
  29. ],
  30. attrgetter("headline")
  31. )
  32. # Getting a single item should work too:
  33. self.assertEqual(Article.objects.all()[0], self.a4)
  34. def test_default_ordering_override(self):
  35. """
  36. Override ordering with order_by, which is in the same format as the
  37. ordering attribute in models.
  38. """
  39. self.assertQuerysetEqual(
  40. Article.objects.order_by("headline"), [
  41. "Article 1",
  42. "Article 2",
  43. "Article 3",
  44. "Article 4",
  45. ],
  46. attrgetter("headline")
  47. )
  48. self.assertQuerysetEqual(
  49. Article.objects.order_by("pub_date", "-headline"), [
  50. "Article 1",
  51. "Article 3",
  52. "Article 2",
  53. "Article 4",
  54. ],
  55. attrgetter("headline")
  56. )
  57. def test_order_by_override(self):
  58. """
  59. Only the last order_by has any effect (since they each override any
  60. previous ordering).
  61. """
  62. self.assertQuerysetEqual(
  63. Article.objects.order_by("id"), [
  64. "Article 1",
  65. "Article 2",
  66. "Article 3",
  67. "Article 4",
  68. ],
  69. attrgetter("headline")
  70. )
  71. self.assertQuerysetEqual(
  72. Article.objects.order_by("id").order_by("-headline"), [
  73. "Article 4",
  74. "Article 3",
  75. "Article 2",
  76. "Article 1",
  77. ],
  78. attrgetter("headline")
  79. )
  80. def test_order_by_nulls_first_and_last(self):
  81. msg = "nulls_first and nulls_last are mutually exclusive"
  82. with self.assertRaisesMessage(ValueError, msg):
  83. Article.objects.order_by(F("author").desc(nulls_last=True, nulls_first=True))
  84. def test_order_by_nulls_last(self):
  85. Article.objects.filter(headline="Article 3").update(author=self.author_1)
  86. Article.objects.filter(headline="Article 4").update(author=self.author_2)
  87. # asc and desc are chainable with nulls_last.
  88. self.assertSequenceEqual(
  89. Article.objects.order_by(F("author").desc(nulls_last=True)),
  90. [self.a4, self.a3, self.a1, self.a2],
  91. )
  92. self.assertSequenceEqual(
  93. Article.objects.order_by(F("author").asc(nulls_last=True)),
  94. [self.a3, self.a4, self.a1, self.a2],
  95. )
  96. self.assertSequenceEqual(
  97. Article.objects.order_by(Upper("author__name").desc(nulls_last=True)),
  98. [self.a4, self.a3, self.a1, self.a2],
  99. )
  100. self.assertSequenceEqual(
  101. Article.objects.order_by(Upper("author__name").asc(nulls_last=True)),
  102. [self.a3, self.a4, self.a1, self.a2],
  103. )
  104. def test_order_by_nulls_first(self):
  105. Article.objects.filter(headline="Article 3").update(author=self.author_1)
  106. Article.objects.filter(headline="Article 4").update(author=self.author_2)
  107. # asc and desc are chainable with nulls_first.
  108. self.assertSequenceEqual(
  109. Article.objects.order_by(F("author").asc(nulls_first=True)),
  110. [self.a1, self.a2, self.a3, self.a4],
  111. )
  112. self.assertSequenceEqual(
  113. Article.objects.order_by(F("author").desc(nulls_first=True)),
  114. [self.a1, self.a2, self.a4, self.a3],
  115. )
  116. self.assertSequenceEqual(
  117. Article.objects.order_by(Upper("author__name").asc(nulls_first=True)),
  118. [self.a1, self.a2, self.a3, self.a4],
  119. )
  120. self.assertSequenceEqual(
  121. Article.objects.order_by(Upper("author__name").desc(nulls_first=True)),
  122. [self.a1, self.a2, self.a4, self.a3],
  123. )
  124. def test_stop_slicing(self):
  125. """
  126. Use the 'stop' part of slicing notation to limit the results.
  127. """
  128. self.assertQuerysetEqual(
  129. Article.objects.order_by("headline")[:2], [
  130. "Article 1",
  131. "Article 2",
  132. ],
  133. attrgetter("headline")
  134. )
  135. def test_stop_start_slicing(self):
  136. """
  137. Use the 'stop' and 'start' parts of slicing notation to offset the
  138. result list.
  139. """
  140. self.assertQuerysetEqual(
  141. Article.objects.order_by("headline")[1:3], [
  142. "Article 2",
  143. "Article 3",
  144. ],
  145. attrgetter("headline")
  146. )
  147. def test_random_ordering(self):
  148. """
  149. Use '?' to order randomly.
  150. """
  151. self.assertEqual(
  152. len(list(Article.objects.order_by("?"))), 4
  153. )
  154. def test_reversed_ordering(self):
  155. """
  156. Ordering can be reversed using the reverse() method on a queryset.
  157. This allows you to extract things like "the last two items" (reverse
  158. and then take the first two).
  159. """
  160. self.assertQuerysetEqual(
  161. Article.objects.all().reverse()[:2], [
  162. "Article 1",
  163. "Article 3",
  164. ],
  165. attrgetter("headline")
  166. )
  167. def test_reverse_ordering_pure(self):
  168. qs1 = Article.objects.order_by(F('headline').asc())
  169. qs2 = qs1.reverse()
  170. self.assertQuerysetEqual(
  171. qs1, [
  172. "Article 1",
  173. "Article 2",
  174. "Article 3",
  175. "Article 4",
  176. ],
  177. attrgetter("headline")
  178. )
  179. self.assertQuerysetEqual(
  180. qs2, [
  181. "Article 4",
  182. "Article 3",
  183. "Article 2",
  184. "Article 1",
  185. ],
  186. attrgetter("headline")
  187. )
  188. def test_no_reordering_after_slicing(self):
  189. msg = 'Cannot reverse a query once a slice has been taken.'
  190. qs = Article.objects.all()[0:2]
  191. with self.assertRaisesMessage(TypeError, msg):
  192. qs.reverse()
  193. with self.assertRaisesMessage(TypeError, msg):
  194. qs.last()
  195. def test_extra_ordering(self):
  196. """
  197. Ordering can be based on fields included from an 'extra' clause
  198. """
  199. self.assertQuerysetEqual(
  200. Article.objects.extra(select={"foo": "pub_date"}, order_by=["foo", "headline"]), [
  201. "Article 1",
  202. "Article 2",
  203. "Article 3",
  204. "Article 4",
  205. ],
  206. attrgetter("headline")
  207. )
  208. def test_extra_ordering_quoting(self):
  209. """
  210. If the extra clause uses an SQL keyword for a name, it will be
  211. protected by quoting.
  212. """
  213. self.assertQuerysetEqual(
  214. Article.objects.extra(select={"order": "pub_date"}, order_by=["order", "headline"]), [
  215. "Article 1",
  216. "Article 2",
  217. "Article 3",
  218. "Article 4",
  219. ],
  220. attrgetter("headline")
  221. )
  222. def test_extra_ordering_with_table_name(self):
  223. self.assertQuerysetEqual(
  224. Article.objects.extra(order_by=['ordering_article.headline']), [
  225. "Article 1",
  226. "Article 2",
  227. "Article 3",
  228. "Article 4",
  229. ],
  230. attrgetter("headline")
  231. )
  232. self.assertQuerysetEqual(
  233. Article.objects.extra(order_by=['-ordering_article.headline']), [
  234. "Article 4",
  235. "Article 3",
  236. "Article 2",
  237. "Article 1",
  238. ],
  239. attrgetter("headline")
  240. )
  241. def test_order_by_pk(self):
  242. """
  243. 'pk' works as an ordering option in Meta.
  244. """
  245. self.assertQuerysetEqual(
  246. Author.objects.all(),
  247. list(reversed(range(1, Author.objects.count() + 1))),
  248. attrgetter("pk"),
  249. )
  250. def test_order_by_fk_attname(self):
  251. """
  252. ordering by a foreign key by its attribute name prevents the query
  253. from inheriting its related model ordering option (#19195).
  254. """
  255. for i in range(1, 5):
  256. author = Author.objects.get(pk=i)
  257. article = getattr(self, "a%d" % (5 - i))
  258. article.author = author
  259. article.save(update_fields={'author'})
  260. self.assertQuerysetEqual(
  261. Article.objects.order_by('author_id'), [
  262. "Article 4",
  263. "Article 3",
  264. "Article 2",
  265. "Article 1",
  266. ],
  267. attrgetter("headline")
  268. )
  269. def test_order_by_f_expression(self):
  270. self.assertQuerysetEqual(
  271. Article.objects.order_by(F('headline')), [
  272. "Article 1",
  273. "Article 2",
  274. "Article 3",
  275. "Article 4",
  276. ],
  277. attrgetter("headline")
  278. )
  279. self.assertQuerysetEqual(
  280. Article.objects.order_by(F('headline').asc()), [
  281. "Article 1",
  282. "Article 2",
  283. "Article 3",
  284. "Article 4",
  285. ],
  286. attrgetter("headline")
  287. )
  288. self.assertQuerysetEqual(
  289. Article.objects.order_by(F('headline').desc()), [
  290. "Article 4",
  291. "Article 3",
  292. "Article 2",
  293. "Article 1",
  294. ],
  295. attrgetter("headline")
  296. )
  297. def test_order_by_f_expression_duplicates(self):
  298. """
  299. A column may only be included once (the first occurrence) so we check
  300. to ensure there are no duplicates by inspecting the SQL.
  301. """
  302. qs = Article.objects.order_by(F('headline').asc(), F('headline').desc())
  303. sql = str(qs.query).upper()
  304. fragment = sql[sql.find('ORDER BY'):]
  305. self.assertEqual(fragment.count('HEADLINE'), 1)
  306. self.assertQuerysetEqual(
  307. qs, [
  308. "Article 1",
  309. "Article 2",
  310. "Article 3",
  311. "Article 4",
  312. ],
  313. attrgetter("headline")
  314. )
  315. qs = Article.objects.order_by(F('headline').desc(), F('headline').asc())
  316. sql = str(qs.query).upper()
  317. fragment = sql[sql.find('ORDER BY'):]
  318. self.assertEqual(fragment.count('HEADLINE'), 1)
  319. self.assertQuerysetEqual(
  320. qs, [
  321. "Article 4",
  322. "Article 3",
  323. "Article 2",
  324. "Article 1",
  325. ],
  326. attrgetter("headline")
  327. )
  328. def test_related_ordering_duplicate_table_reference(self):
  329. """
  330. An ordering referencing a model with an ordering referencing a model
  331. multiple time no circular reference should be detected (#24654).
  332. """
  333. first_author = Author.objects.create()
  334. second_author = Author.objects.create()
  335. self.a1.author = first_author
  336. self.a1.second_author = second_author
  337. self.a1.save()
  338. self.a2.author = second_author
  339. self.a2.second_author = first_author
  340. self.a2.save()
  341. r1 = Reference.objects.create(article_id=self.a1.pk)
  342. r2 = Reference.objects.create(article_id=self.a2.pk)
  343. self.assertSequenceEqual(Reference.objects.all(), [r2, r1])