tests.py 14 KB

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