tests.py 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. # -*- encoding: utf-8 -*-
  2. from __future__ import unicode_literals
  3. from datetime import datetime
  4. from operator import attrgetter
  5. from django.db.models import Q
  6. from django.test import TestCase
  7. from django.utils.encoding import force_str
  8. from .models import Article
  9. class OrLookupsTests(TestCase):
  10. def setUp(self):
  11. self.a1 = Article.objects.create(
  12. headline='Hello', pub_date=datetime(2005, 11, 27)
  13. ).pk
  14. self.a2 = Article.objects.create(
  15. headline='Goodbye', pub_date=datetime(2005, 11, 28)
  16. ).pk
  17. self.a3 = Article.objects.create(
  18. headline='Hello and goodbye', pub_date=datetime(2005, 11, 29)
  19. ).pk
  20. def test_filter_or(self):
  21. self.assertQuerysetEqual(
  22. (
  23. Article.objects.filter(headline__startswith='Hello') |
  24. Article.objects.filter(headline__startswith='Goodbye')
  25. ), [
  26. 'Hello',
  27. 'Goodbye',
  28. 'Hello and goodbye'
  29. ],
  30. attrgetter("headline")
  31. )
  32. self.assertQuerysetEqual(
  33. Article.objects.filter(headline__contains='Hello') | Article.objects.filter(headline__contains='bye'), [
  34. 'Hello',
  35. 'Goodbye',
  36. 'Hello and goodbye'
  37. ],
  38. attrgetter("headline")
  39. )
  40. self.assertQuerysetEqual(
  41. Article.objects.filter(headline__iexact='Hello') | Article.objects.filter(headline__contains='ood'), [
  42. 'Hello',
  43. 'Goodbye',
  44. 'Hello and goodbye'
  45. ],
  46. attrgetter("headline")
  47. )
  48. self.assertQuerysetEqual(
  49. Article.objects.filter(Q(headline__startswith='Hello') | Q(headline__startswith='Goodbye')), [
  50. 'Hello',
  51. 'Goodbye',
  52. 'Hello and goodbye'
  53. ],
  54. attrgetter("headline")
  55. )
  56. def test_stages(self):
  57. # You can shorten this syntax with code like the following, which is
  58. # especially useful if building the query in stages:
  59. articles = Article.objects.all()
  60. self.assertQuerysetEqual(
  61. articles.filter(headline__startswith='Hello') & articles.filter(headline__startswith='Goodbye'),
  62. []
  63. )
  64. self.assertQuerysetEqual(
  65. articles.filter(headline__startswith='Hello') & articles.filter(headline__contains='bye'), [
  66. 'Hello and goodbye'
  67. ],
  68. attrgetter("headline")
  69. )
  70. def test_pk_q(self):
  71. self.assertQuerysetEqual(
  72. Article.objects.filter(Q(pk=self.a1) | Q(pk=self.a2)), [
  73. 'Hello',
  74. 'Goodbye'
  75. ],
  76. attrgetter("headline")
  77. )
  78. self.assertQuerysetEqual(
  79. Article.objects.filter(Q(pk=self.a1) | Q(pk=self.a2) | Q(pk=self.a3)), [
  80. 'Hello',
  81. 'Goodbye',
  82. 'Hello and goodbye'
  83. ],
  84. attrgetter("headline"),
  85. )
  86. def test_pk_in(self):
  87. self.assertQuerysetEqual(
  88. Article.objects.filter(pk__in=[self.a1, self.a2, self.a3]), [
  89. 'Hello',
  90. 'Goodbye',
  91. 'Hello and goodbye'
  92. ],
  93. attrgetter("headline"),
  94. )
  95. self.assertQuerysetEqual(
  96. Article.objects.filter(pk__in=(self.a1, self.a2, self.a3)), [
  97. 'Hello',
  98. 'Goodbye',
  99. 'Hello and goodbye'
  100. ],
  101. attrgetter("headline"),
  102. )
  103. self.assertQuerysetEqual(
  104. Article.objects.filter(pk__in=[self.a1, self.a2, self.a3, 40000]), [
  105. 'Hello',
  106. 'Goodbye',
  107. 'Hello and goodbye'
  108. ],
  109. attrgetter("headline"),
  110. )
  111. def test_q_repr(self):
  112. or_expr = Q(baz=Article(headline="Foö"))
  113. self.assertEqual(repr(or_expr), force_str("<Q: (AND: ('baz', <Article: Foö>))>"))
  114. negated_or = ~Q(baz=Article(headline="Foö"))
  115. self.assertEqual(repr(negated_or), force_str("<Q: (NOT (AND: ('baz', <Article: Foö>)))>"))
  116. def test_q_negated(self):
  117. # Q objects can be negated
  118. self.assertQuerysetEqual(
  119. Article.objects.filter(Q(pk=self.a1) | ~Q(pk=self.a2)), [
  120. 'Hello',
  121. 'Hello and goodbye'
  122. ],
  123. attrgetter("headline")
  124. )
  125. self.assertQuerysetEqual(
  126. Article.objects.filter(~Q(pk=self.a1) & ~Q(pk=self.a2)), [
  127. 'Hello and goodbye'
  128. ],
  129. attrgetter("headline"),
  130. )
  131. # This allows for more complex queries than filter() and exclude()
  132. # alone would allow
  133. self.assertQuerysetEqual(
  134. Article.objects.filter(Q(pk=self.a1) & (~Q(pk=self.a2) | Q(pk=self.a3))), [
  135. 'Hello'
  136. ],
  137. attrgetter("headline"),
  138. )
  139. def test_complex_filter(self):
  140. # The 'complex_filter' method supports framework features such as
  141. # 'limit_choices_to' which normally take a single dictionary of lookup
  142. # arguments but need to support arbitrary queries via Q objects too.
  143. self.assertQuerysetEqual(
  144. Article.objects.complex_filter({'pk': self.a1}), [
  145. 'Hello'
  146. ],
  147. attrgetter("headline"),
  148. )
  149. self.assertQuerysetEqual(
  150. Article.objects.complex_filter(Q(pk=self.a1) | Q(pk=self.a2)), [
  151. 'Hello',
  152. 'Goodbye'
  153. ],
  154. attrgetter("headline"),
  155. )
  156. def test_empty_in(self):
  157. # Passing "in" an empty list returns no results ...
  158. self.assertQuerysetEqual(
  159. Article.objects.filter(pk__in=[]),
  160. []
  161. )
  162. # ... but can return results if we OR it with another query.
  163. self.assertQuerysetEqual(
  164. Article.objects.filter(Q(pk__in=[]) | Q(headline__icontains='goodbye')), [
  165. 'Goodbye',
  166. 'Hello and goodbye'
  167. ],
  168. attrgetter("headline"),
  169. )
  170. def test_q_and(self):
  171. # Q arg objects are ANDed
  172. self.assertQuerysetEqual(
  173. Article.objects.filter(Q(headline__startswith='Hello'), Q(headline__contains='bye')), [
  174. 'Hello and goodbye'
  175. ],
  176. attrgetter("headline")
  177. )
  178. # Q arg AND order is irrelevant
  179. self.assertQuerysetEqual(
  180. Article.objects.filter(Q(headline__contains='bye'), headline__startswith='Hello'), [
  181. 'Hello and goodbye'
  182. ],
  183. attrgetter("headline"),
  184. )
  185. self.assertQuerysetEqual(
  186. Article.objects.filter(Q(headline__startswith='Hello') & Q(headline__startswith='Goodbye')),
  187. []
  188. )
  189. def test_q_exclude(self):
  190. self.assertQuerysetEqual(
  191. Article.objects.exclude(Q(headline__startswith='Hello')), [
  192. 'Goodbye'
  193. ],
  194. attrgetter("headline")
  195. )
  196. def test_other_arg_queries(self):
  197. # Try some arg queries with operations other than filter.
  198. self.assertEqual(
  199. Article.objects.get(Q(headline__startswith='Hello'), Q(headline__contains='bye')).headline,
  200. 'Hello and goodbye'
  201. )
  202. self.assertEqual(
  203. Article.objects.filter(Q(headline__startswith='Hello') | Q(headline__contains='bye')).count(),
  204. 3
  205. )
  206. self.assertSequenceEqual(
  207. Article.objects.filter(Q(headline__startswith='Hello'), Q(headline__contains='bye')).values(), [
  208. {"headline": "Hello and goodbye", "id": self.a3, "pub_date": datetime(2005, 11, 29)},
  209. ],
  210. )
  211. self.assertEqual(
  212. Article.objects.filter(Q(headline__startswith='Hello')).in_bulk([self.a1, self.a2]),
  213. {self.a1: Article.objects.get(pk=self.a1)}
  214. )