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