tests.py 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. from __future__ import unicode_literals
  2. from datetime import datetime
  3. from operator import attrgetter
  4. from django.db.models import F
  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()
  15. cls.author_2 = Author.objects.create()
  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_stop_slicing(self):
  81. """
  82. Use the 'stop' part of slicing notation to limit the results.
  83. """
  84. self.assertQuerysetEqual(
  85. Article.objects.order_by("headline")[:2], [
  86. "Article 1",
  87. "Article 2",
  88. ],
  89. attrgetter("headline")
  90. )
  91. def test_stop_start_slicing(self):
  92. """
  93. Use the 'stop' and 'start' parts of slicing notation to offset the
  94. result list.
  95. """
  96. self.assertQuerysetEqual(
  97. Article.objects.order_by("headline")[1:3], [
  98. "Article 2",
  99. "Article 3",
  100. ],
  101. attrgetter("headline")
  102. )
  103. def test_random_ordering(self):
  104. """
  105. Use '?' to order randomly.
  106. """
  107. self.assertEqual(
  108. len(list(Article.objects.order_by("?"))), 4
  109. )
  110. def test_reversed_ordering(self):
  111. """
  112. Ordering can be reversed using the reverse() method on a queryset.
  113. This allows you to extract things like "the last two items" (reverse
  114. and then take the first two).
  115. """
  116. self.assertQuerysetEqual(
  117. Article.objects.all().reverse()[:2], [
  118. "Article 1",
  119. "Article 3",
  120. ],
  121. attrgetter("headline")
  122. )
  123. def test_reverse_ordering_pure(self):
  124. qs1 = Article.objects.order_by(F('headline').asc())
  125. qs2 = qs1.reverse()
  126. self.assertQuerysetEqual(
  127. qs1, [
  128. "Article 1",
  129. "Article 2",
  130. "Article 3",
  131. "Article 4",
  132. ],
  133. attrgetter("headline")
  134. )
  135. self.assertQuerysetEqual(
  136. qs2, [
  137. "Article 4",
  138. "Article 3",
  139. "Article 2",
  140. "Article 1",
  141. ],
  142. attrgetter("headline")
  143. )
  144. def test_extra_ordering(self):
  145. """
  146. Ordering can be based on fields included from an 'extra' clause
  147. """
  148. self.assertQuerysetEqual(
  149. Article.objects.extra(select={"foo": "pub_date"}, order_by=["foo", "headline"]), [
  150. "Article 1",
  151. "Article 2",
  152. "Article 3",
  153. "Article 4",
  154. ],
  155. attrgetter("headline")
  156. )
  157. def test_extra_ordering_quoting(self):
  158. """
  159. If the extra clause uses an SQL keyword for a name, it will be
  160. protected by quoting.
  161. """
  162. self.assertQuerysetEqual(
  163. Article.objects.extra(select={"order": "pub_date"}, order_by=["order", "headline"]), [
  164. "Article 1",
  165. "Article 2",
  166. "Article 3",
  167. "Article 4",
  168. ],
  169. attrgetter("headline")
  170. )
  171. def test_extra_ordering_with_table_name(self):
  172. self.assertQuerysetEqual(
  173. Article.objects.extra(order_by=['ordering_article.headline']), [
  174. "Article 1",
  175. "Article 2",
  176. "Article 3",
  177. "Article 4",
  178. ],
  179. attrgetter("headline")
  180. )
  181. self.assertQuerysetEqual(
  182. Article.objects.extra(order_by=['-ordering_article.headline']), [
  183. "Article 4",
  184. "Article 3",
  185. "Article 2",
  186. "Article 1",
  187. ],
  188. attrgetter("headline")
  189. )
  190. def test_order_by_pk(self):
  191. """
  192. 'pk' works as an ordering option in Meta.
  193. """
  194. self.assertQuerysetEqual(
  195. Author.objects.all(),
  196. list(reversed(range(1, Author.objects.count() + 1))),
  197. attrgetter("pk"),
  198. )
  199. def test_order_by_fk_attname(self):
  200. """
  201. ordering by a foreign key by its attribute name prevents the query
  202. from inheriting its related model ordering option (#19195).
  203. """
  204. for i in range(1, 5):
  205. author = Author.objects.get(pk=i)
  206. article = getattr(self, "a%d" % (5 - i))
  207. article.author = author
  208. article.save(update_fields={'author'})
  209. self.assertQuerysetEqual(
  210. Article.objects.order_by('author_id'), [
  211. "Article 4",
  212. "Article 3",
  213. "Article 2",
  214. "Article 1",
  215. ],
  216. attrgetter("headline")
  217. )
  218. def test_order_by_f_expression(self):
  219. self.assertQuerysetEqual(
  220. Article.objects.order_by(F('headline')), [
  221. "Article 1",
  222. "Article 2",
  223. "Article 3",
  224. "Article 4",
  225. ],
  226. attrgetter("headline")
  227. )
  228. self.assertQuerysetEqual(
  229. Article.objects.order_by(F('headline').asc()), [
  230. "Article 1",
  231. "Article 2",
  232. "Article 3",
  233. "Article 4",
  234. ],
  235. attrgetter("headline")
  236. )
  237. self.assertQuerysetEqual(
  238. Article.objects.order_by(F('headline').desc()), [
  239. "Article 4",
  240. "Article 3",
  241. "Article 2",
  242. "Article 1",
  243. ],
  244. attrgetter("headline")
  245. )
  246. def test_order_by_f_expression_duplicates(self):
  247. """
  248. A column may only be included once (the first occurrence) so we check
  249. to ensure there are no duplicates by inspecting the SQL.
  250. """
  251. qs = Article.objects.order_by(F('headline').asc(), F('headline').desc())
  252. sql = str(qs.query).upper()
  253. fragment = sql[sql.find('ORDER BY'):]
  254. self.assertEqual(fragment.count('HEADLINE'), 1)
  255. self.assertQuerysetEqual(
  256. qs, [
  257. "Article 1",
  258. "Article 2",
  259. "Article 3",
  260. "Article 4",
  261. ],
  262. attrgetter("headline")
  263. )
  264. qs = Article.objects.order_by(F('headline').desc(), F('headline').asc())
  265. sql = str(qs.query).upper()
  266. fragment = sql[sql.find('ORDER BY'):]
  267. self.assertEqual(fragment.count('HEADLINE'), 1)
  268. self.assertQuerysetEqual(
  269. qs, [
  270. "Article 4",
  271. "Article 3",
  272. "Article 2",
  273. "Article 1",
  274. ],
  275. attrgetter("headline")
  276. )
  277. def test_related_ordering_duplicate_table_reference(self):
  278. """
  279. An ordering referencing a model with an ordering referencing a model
  280. multiple time no circular reference should be detected (#24654).
  281. """
  282. first_author = Author.objects.create()
  283. second_author = Author.objects.create()
  284. self.a1.author = first_author
  285. self.a1.second_author = second_author
  286. self.a1.save()
  287. self.a2.author = second_author
  288. self.a2.second_author = first_author
  289. self.a2.save()
  290. r1 = Reference.objects.create(article_id=self.a1.pk)
  291. r2 = Reference.objects.create(article_id=self.a2.pk)
  292. self.assertSequenceEqual(Reference.objects.all(), [r2, r1])