|
1 from django.test import TestCase |
|
2 |
|
3 from django.utils.datastructures import SortedDict |
|
4 |
|
5 from django.contrib.auth.models import User |
|
6 from regressiontests.extra_regress.models import TestObject, Order, \ |
|
7 RevisionableModel |
|
8 |
|
9 import datetime |
|
10 |
|
11 class ExtraRegressTests(TestCase): |
|
12 |
|
13 def setUp(self): |
|
14 self.u = User.objects.create_user( |
|
15 username="fred", |
|
16 password="secret", |
|
17 email="fred@example.com" |
|
18 ) |
|
19 |
|
20 def test_regression_7314_7372(self): |
|
21 """ |
|
22 Regression tests for #7314 and #7372 |
|
23 """ |
|
24 rm = RevisionableModel.objects.create( |
|
25 title='First Revision', |
|
26 when=datetime.datetime(2008, 9, 28, 10, 30, 0) |
|
27 ) |
|
28 self.assertEqual(rm.pk, rm.base.pk) |
|
29 |
|
30 rm2 = rm.new_revision() |
|
31 rm2.title = "Second Revision" |
|
32 rm.when = datetime.datetime(2008, 9, 28, 14, 25, 0) |
|
33 rm2.save() |
|
34 |
|
35 self.assertEqual(rm2.title, 'Second Revision') |
|
36 self.assertEqual(rm2.base.title, 'First Revision') |
|
37 |
|
38 self.assertNotEqual(rm2.pk, rm.pk) |
|
39 self.assertEqual(rm2.base.pk, rm.pk) |
|
40 |
|
41 # Queryset to match most recent revision: |
|
42 qs = RevisionableModel.objects.extra( |
|
43 where=["%(table)s.id IN (SELECT MAX(rev.id) FROM %(table)s rev GROUP BY rev.base_id)" % { |
|
44 'table': RevisionableModel._meta.db_table, |
|
45 }] |
|
46 ) |
|
47 |
|
48 self.assertQuerysetEqual(qs, |
|
49 [('Second Revision', 'First Revision')], |
|
50 transform=lambda r: (r.title, r.base.title) |
|
51 ) |
|
52 |
|
53 # Queryset to search for string in title: |
|
54 qs2 = RevisionableModel.objects.filter(title__contains="Revision") |
|
55 self.assertQuerysetEqual(qs2, |
|
56 [ |
|
57 ('First Revision', 'First Revision'), |
|
58 ('Second Revision', 'First Revision'), |
|
59 ], |
|
60 transform=lambda r: (r.title, r.base.title) |
|
61 ) |
|
62 |
|
63 # Following queryset should return the most recent revision: |
|
64 self.assertQuerysetEqual(qs & qs2, |
|
65 [('Second Revision', 'First Revision')], |
|
66 transform=lambda r: (r.title, r.base.title) |
|
67 ) |
|
68 |
|
69 def test_extra_stay_tied(self): |
|
70 # Extra select parameters should stay tied to their corresponding |
|
71 # select portions. Applies when portions are updated or otherwise |
|
72 # moved around. |
|
73 qs = User.objects.extra( |
|
74 select=SortedDict((("alpha", "%s"), ("beta", "2"), ("gamma", "%s"))), |
|
75 select_params=(1, 3) |
|
76 ) |
|
77 qs = qs.extra(select={"beta": 4}) |
|
78 qs = qs.extra(select={"alpha": "%s"}, select_params=[5]) |
|
79 self.assertEqual( |
|
80 list(qs.filter(id=self.u.id).values('alpha', 'beta', 'gamma')), |
|
81 [{'alpha': 5, 'beta': 4, 'gamma': 3}] |
|
82 ) |
|
83 |
|
84 def test_regression_7957(self): |
|
85 """ |
|
86 Regression test for #7957: Combining extra() calls should leave the |
|
87 corresponding parameters associated with the right extra() bit. I.e. |
|
88 internal dictionary must remain sorted. |
|
89 """ |
|
90 self.assertEqual( |
|
91 User.objects.extra(select={"alpha": "%s"}, select_params=(1,) |
|
92 ).extra(select={"beta": "%s"}, select_params=(2,))[0].alpha, |
|
93 1) |
|
94 |
|
95 self.assertEqual( |
|
96 User.objects.extra(select={"beta": "%s"}, select_params=(1,) |
|
97 ).extra(select={"alpha": "%s"}, select_params=(2,))[0].alpha, |
|
98 2) |
|
99 |
|
100 def test_regression_7961(self): |
|
101 """ |
|
102 Regression test for #7961: When not using a portion of an |
|
103 extra(...) in a query, remove any corresponding parameters from the |
|
104 query as well. |
|
105 """ |
|
106 self.assertEqual( |
|
107 list(User.objects.extra(select={"alpha": "%s"}, select_params=(-6,) |
|
108 ).filter(id=self.u.id).values_list('id', flat=True)), |
|
109 [self.u.id] |
|
110 ) |
|
111 |
|
112 def test_regression_8063(self): |
|
113 """ |
|
114 Regression test for #8063: limiting a query shouldn't discard any |
|
115 extra() bits. |
|
116 """ |
|
117 qs = User.objects.all().extra(where=['id=%s'], params=[self.u.id]) |
|
118 self.assertQuerysetEqual(qs, ['<User: fred>']) |
|
119 self.assertQuerysetEqual(qs[:1], ['<User: fred>']) |
|
120 |
|
121 def test_regression_8039(self): |
|
122 """ |
|
123 Regression test for #8039: Ordering sometimes removed relevant tables |
|
124 from extra(). This test is the critical case: ordering uses a table, |
|
125 but then removes the reference because of an optimisation. The table |
|
126 should still be present because of the extra() call. |
|
127 """ |
|
128 self.assertQuerysetEqual( |
|
129 Order.objects.extra(where=["username=%s"], |
|
130 params=["fred"], |
|
131 tables=["auth_user"] |
|
132 ).order_by('created_by'), |
|
133 [] |
|
134 ) |
|
135 |
|
136 def test_regression_8819(self): |
|
137 """ |
|
138 Regression test for #8819: Fields in the extra(select=...) list |
|
139 should be available to extra(order_by=...). |
|
140 """ |
|
141 self.assertQuerysetEqual( |
|
142 User.objects.filter(pk=self.u.id).extra(select={'extra_field': 1}).distinct(), |
|
143 ['<User: fred>'] |
|
144 ) |
|
145 self.assertQuerysetEqual( |
|
146 User.objects.filter(pk=self.u.id).extra(select={'extra_field': 1}, order_by=['extra_field']), |
|
147 ['<User: fred>'] |
|
148 ) |
|
149 self.assertQuerysetEqual( |
|
150 User.objects.filter(pk=self.u.id).extra(select={'extra_field': 1}, order_by=['extra_field']).distinct(), |
|
151 ['<User: fred>'] |
|
152 ) |
|
153 |
|
154 def test_dates_query(self): |
|
155 """ |
|
156 When calling the dates() method on a queryset with extra selection |
|
157 columns, we can (and should) ignore those columns. They don't change |
|
158 the result and cause incorrect SQL to be produced otherwise. |
|
159 """ |
|
160 rm = RevisionableModel.objects.create( |
|
161 title='First Revision', |
|
162 when=datetime.datetime(2008, 9, 28, 10, 30, 0) |
|
163 ) |
|
164 |
|
165 self.assertQuerysetEqual( |
|
166 RevisionableModel.objects.extra(select={"the_answer": 'id'}).dates('when', 'month'), |
|
167 ['datetime.datetime(2008, 9, 1, 0, 0)'] |
|
168 ) |
|
169 |
|
170 def test_values_with_extra(self): |
|
171 """ |
|
172 Regression test for #10256... If there is a values() clause, Extra |
|
173 columns are only returned if they are explicitly mentioned. |
|
174 """ |
|
175 obj = TestObject(first='first', second='second', third='third') |
|
176 obj.save() |
|
177 |
|
178 self.assertEqual( |
|
179 list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values()), |
|
180 [{'bar': u'second', 'third': u'third', 'second': u'second', 'whiz': u'third', 'foo': u'first', 'id': obj.pk, 'first': u'first'}] |
|
181 ) |
|
182 |
|
183 # Extra clauses after an empty values clause are still included |
|
184 self.assertEqual( |
|
185 list(TestObject.objects.values().extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third'))))), |
|
186 [{'bar': u'second', 'third': u'third', 'second': u'second', 'whiz': u'third', 'foo': u'first', 'id': obj.pk, 'first': u'first'}] |
|
187 ) |
|
188 |
|
189 # Extra columns are ignored if not mentioned in the values() clause |
|
190 self.assertEqual( |
|
191 list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values('first', 'second')), |
|
192 [{'second': u'second', 'first': u'first'}] |
|
193 ) |
|
194 |
|
195 # Extra columns after a non-empty values() clause are ignored |
|
196 self.assertEqual( |
|
197 list(TestObject.objects.values('first', 'second').extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third'))))), |
|
198 [{'second': u'second', 'first': u'first'}] |
|
199 ) |
|
200 |
|
201 # Extra columns can be partially returned |
|
202 self.assertEqual( |
|
203 list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values('first', 'second', 'foo')), |
|
204 [{'second': u'second', 'foo': u'first', 'first': u'first'}] |
|
205 ) |
|
206 |
|
207 # Also works if only extra columns are included |
|
208 self.assertEqual( |
|
209 list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values('foo', 'whiz')), |
|
210 [{'foo': u'first', 'whiz': u'third'}] |
|
211 ) |
|
212 |
|
213 # Values list works the same way |
|
214 # All columns are returned for an empty values_list() |
|
215 self.assertEqual( |
|
216 list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list()), |
|
217 [(u'first', u'second', u'third', obj.pk, u'first', u'second', u'third')] |
|
218 ) |
|
219 |
|
220 # Extra columns after an empty values_list() are still included |
|
221 self.assertEqual( |
|
222 list(TestObject.objects.values_list().extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third'))))), |
|
223 [(u'first', u'second', u'third', obj.pk, u'first', u'second', u'third')] |
|
224 ) |
|
225 |
|
226 # Extra columns ignored completely if not mentioned in values_list() |
|
227 self.assertEqual( |
|
228 list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('first', 'second')), |
|
229 [(u'first', u'second')] |
|
230 ) |
|
231 |
|
232 # Extra columns after a non-empty values_list() clause are ignored completely |
|
233 self.assertEqual( |
|
234 list(TestObject.objects.values_list('first', 'second').extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third'))))), |
|
235 [(u'first', u'second')] |
|
236 ) |
|
237 |
|
238 self.assertEqual( |
|
239 list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('second', flat=True)), |
|
240 [u'second'] |
|
241 ) |
|
242 |
|
243 # Only the extra columns specified in the values_list() are returned |
|
244 self.assertEqual( |
|
245 list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('first', 'second', 'whiz')), |
|
246 [(u'first', u'second', u'third')] |
|
247 ) |
|
248 |
|
249 # ...also works if only extra columns are included |
|
250 self.assertEqual( |
|
251 list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('foo','whiz')), |
|
252 [(u'first', u'third')] |
|
253 ) |
|
254 |
|
255 self.assertEqual( |
|
256 list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('whiz', flat=True)), |
|
257 [u'third'] |
|
258 ) |
|
259 |
|
260 # ... and values are returned in the order they are specified |
|
261 self.assertEqual( |
|
262 list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('whiz','foo')), |
|
263 [(u'third', u'first')] |
|
264 ) |
|
265 |
|
266 self.assertEqual( |
|
267 list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('first','id')), |
|
268 [(u'first', obj.pk)] |
|
269 ) |
|
270 |
|
271 self.assertEqual( |
|
272 list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('whiz', 'first', 'bar', 'id')), |
|
273 [(u'third', u'first', u'second', obj.pk)] |
|
274 ) |
|
275 |
|
276 def test_regression_10847(self): |
|
277 """ |
|
278 Regression for #10847: the list of extra columns can always be |
|
279 accurately evaluated. Using an inner query ensures that as_sql() is |
|
280 producing correct output without requiring full evaluation and |
|
281 execution of the inner query. |
|
282 """ |
|
283 obj = TestObject(first='first', second='second', third='third') |
|
284 obj.save() |
|
285 |
|
286 self.assertEqual( |
|
287 list(TestObject.objects.extra(select={'extra': 1}).values('pk')), |
|
288 [{'pk': obj.pk}] |
|
289 ) |
|
290 |
|
291 self.assertQuerysetEqual( |
|
292 TestObject.objects.filter( |
|
293 pk__in=TestObject.objects.extra(select={'extra': 1}).values('pk') |
|
294 ), |
|
295 ['<TestObject: TestObject: first,second,third>'] |
|
296 ) |
|
297 |
|
298 self.assertEqual( |
|
299 list(TestObject.objects.values('pk').extra(select={'extra': 1})), |
|
300 [{'pk': obj.pk}] |
|
301 ) |
|
302 |
|
303 self.assertQuerysetEqual( |
|
304 TestObject.objects.filter( |
|
305 pk__in=TestObject.objects.values('pk').extra(select={'extra': 1}) |
|
306 ), |
|
307 ['<TestObject: TestObject: first,second,third>'] |
|
308 ) |
|
309 |
|
310 self.assertQuerysetEqual( |
|
311 TestObject.objects.filter(pk=obj.pk) | |
|
312 TestObject.objects.extra(where=["id > %s"], params=[obj.pk]), |
|
313 ['<TestObject: TestObject: first,second,third>'] |
|
314 ) |