1 from __future__
import absolute_import
5 from django
.contrib
.auth
.models
import User
6 from django
.test
import TestCase
7 from django
.utils
.datastructures
import SortedDict
9 from .models
import TestObject
, Order
, RevisionableModel
12 class ExtraRegressTests(TestCase
):
15 self
.u
= User
.objects
.create_user(
18 email
="fred@example.com"
21 def test_regression_7314_7372(self
):
23 Regression tests for #7314 and #7372
25 rm
= RevisionableModel
.objects
.create(
26 title
='First Revision',
27 when
=datetime
.datetime(2008, 9, 28, 10, 30, 0)
29 self
.assertEqual(rm
.pk
, rm
.base
.pk
)
31 rm2
= rm
.new_revision()
32 rm2
.title
= "Second Revision"
33 rm
.when
= datetime
.datetime(2008, 9, 28, 14, 25, 0)
36 self
.assertEqual(rm2
.title
, 'Second Revision')
37 self
.assertEqual(rm2
.base
.title
, 'First Revision')
39 self
.assertNotEqual(rm2
.pk
, rm
.pk
)
40 self
.assertEqual(rm2
.base
.pk
, rm
.pk
)
42 # Queryset to match most recent revision:
43 qs
= RevisionableModel
.objects
.extra(
44 where
=["%(table)s.id IN (SELECT MAX(rev.id) FROM %(table)s rev GROUP BY rev.base_id)" % {
45 'table': RevisionableModel
._meta
.db_table
,
49 self
.assertQuerysetEqual(qs
,
50 [('Second Revision', 'First Revision')],
51 transform
=lambda r
: (r
.title
, r
.base
.title
)
54 # Queryset to search for string in title:
55 qs2
= RevisionableModel
.objects
.filter(title__contains
="Revision")
56 self
.assertQuerysetEqual(qs2
,
58 ('First Revision', 'First Revision'),
59 ('Second Revision', 'First Revision'),
61 transform
=lambda r
: (r
.title
, r
.base
.title
)
64 # Following queryset should return the most recent revision:
65 self
.assertQuerysetEqual(qs
& qs2
,
66 [('Second Revision', 'First Revision')],
67 transform
=lambda r
: (r
.title
, r
.base
.title
)
70 def test_extra_stay_tied(self
):
71 # Extra select parameters should stay tied to their corresponding
72 # select portions. Applies when portions are updated or otherwise
74 qs
= User
.objects
.extra(
75 select
=SortedDict((("alpha", "%s"), ("beta", "2"), ("gamma", "%s"))),
78 qs
= qs
.extra(select
={"beta": 4})
79 qs
= qs
.extra(select
={"alpha": "%s"}, select_params
=[5])
81 list(qs
.filter(id=self
.u
.id).values('alpha', 'beta', 'gamma')),
82 [{'alpha': 5, 'beta': 4, 'gamma': 3}]
85 def test_regression_7957(self
):
87 Regression test for #7957: Combining extra() calls should leave the
88 corresponding parameters associated with the right extra() bit. I.e.
89 internal dictionary must remain sorted.
92 User
.objects
.extra(select
={"alpha": "%s"}, select_params
=(1,)
93 ).extra(select
={"beta": "%s"}, select_params
=(2,))[0].alpha
,
97 User
.objects
.extra(select
={"beta": "%s"}, select_params
=(1,)
98 ).extra(select
={"alpha": "%s"}, select_params
=(2,))[0].alpha
,
101 def test_regression_7961(self
):
103 Regression test for #7961: When not using a portion of an
104 extra(...) in a query, remove any corresponding parameters from the
108 list(User
.objects
.extra(select
={"alpha": "%s"}, select_params
=(-6,)
109 ).filter(id=self
.u
.id).values_list('id', flat
=True)),
113 def test_regression_8063(self
):
115 Regression test for #8063: limiting a query shouldn't discard any
118 qs
= User
.objects
.all().extra(where
=['id=%s'], params
=[self
.u
.id])
119 self
.assertQuerysetEqual(qs
, ['<User: fred>'])
120 self
.assertQuerysetEqual(qs
[:1], ['<User: fred>'])
122 def test_regression_8039(self
):
124 Regression test for #8039: Ordering sometimes removed relevant tables
125 from extra(). This test is the critical case: ordering uses a table,
126 but then removes the reference because of an optimisation. The table
127 should still be present because of the extra() call.
129 self
.assertQuerysetEqual(
130 Order
.objects
.extra(where
=["username=%s"],
133 ).order_by('created_by'),
137 def test_regression_8819(self
):
139 Regression test for #8819: Fields in the extra(select=...) list
140 should be available to extra(order_by=...).
142 self
.assertQuerysetEqual(
143 User
.objects
.filter(pk
=self
.u
.id).extra(select
={'extra_field': 1}).distinct(),
146 self
.assertQuerysetEqual(
147 User
.objects
.filter(pk
=self
.u
.id).extra(select
={'extra_field': 1}, order_by
=['extra_field']),
150 self
.assertQuerysetEqual(
151 User
.objects
.filter(pk
=self
.u
.id).extra(select
={'extra_field': 1}, order_by
=['extra_field']).distinct(),
155 def test_dates_query(self
):
157 When calling the dates() method on a queryset with extra selection
158 columns, we can (and should) ignore those columns. They don't change
159 the result and cause incorrect SQL to be produced otherwise.
161 rm
= RevisionableModel
.objects
.create(
162 title
='First Revision',
163 when
=datetime
.datetime(2008, 9, 28, 10, 30, 0)
166 self
.assertQuerysetEqual(
167 RevisionableModel
.objects
.extra(select
={"the_answer": 'id'}).dates('when', 'month'),
168 ['datetime.datetime(2008, 9, 1, 0, 0)']
171 def test_values_with_extra(self
):
173 Regression test for #10256... If there is a values() clause, Extra
174 columns are only returned if they are explicitly mentioned.
176 obj
= TestObject(first
='first', second
='second', third
='third')
180 list(TestObject
.objects
.extra(select
=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values()),
181 [{'bar': u
'second', 'third': u
'third', 'second': u
'second', 'whiz': u
'third', 'foo': u
'first', 'id': obj
.pk
, 'first': u
'first'}]
184 # Extra clauses after an empty values clause are still included
186 list(TestObject
.objects
.values().extra(select
=SortedDict((('foo','first'), ('bar','second'), ('whiz','third'))))),
187 [{'bar': u
'second', 'third': u
'third', 'second': u
'second', 'whiz': u
'third', 'foo': u
'first', 'id': obj
.pk
, 'first': u
'first'}]
190 # Extra columns are ignored if not mentioned in the values() clause
192 list(TestObject
.objects
.extra(select
=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values('first', 'second')),
193 [{'second': u
'second', 'first': u
'first'}]
196 # Extra columns after a non-empty values() clause are ignored
198 list(TestObject
.objects
.values('first', 'second').extra(select
=SortedDict((('foo','first'), ('bar','second'), ('whiz','third'))))),
199 [{'second': u
'second', 'first': u
'first'}]
202 # Extra columns can be partially returned
204 list(TestObject
.objects
.extra(select
=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values('first', 'second', 'foo')),
205 [{'second': u
'second', 'foo': u
'first', 'first': u
'first'}]
208 # Also works if only extra columns are included
210 list(TestObject
.objects
.extra(select
=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values('foo', 'whiz')),
211 [{'foo': u
'first', 'whiz': u
'third'}]
214 # Values list works the same way
215 # All columns are returned for an empty values_list()
217 list(TestObject
.objects
.extra(select
=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list()),
218 [(u
'first', u
'second', u
'third', obj
.pk
, u
'first', u
'second', u
'third')]
221 # Extra columns after an empty values_list() are still included
223 list(TestObject
.objects
.values_list().extra(select
=SortedDict((('foo','first'), ('bar','second'), ('whiz','third'))))),
224 [(u
'first', u
'second', u
'third', obj
.pk
, u
'first', u
'second', u
'third')]
227 # Extra columns ignored completely if not mentioned in values_list()
229 list(TestObject
.objects
.extra(select
=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('first', 'second')),
230 [(u
'first', u
'second')]
233 # Extra columns after a non-empty values_list() clause are ignored completely
235 list(TestObject
.objects
.values_list('first', 'second').extra(select
=SortedDict((('foo','first'), ('bar','second'), ('whiz','third'))))),
236 [(u
'first', u
'second')]
240 list(TestObject
.objects
.extra(select
=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('second', flat
=True)),
244 # Only the extra columns specified in the values_list() are returned
246 list(TestObject
.objects
.extra(select
=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('first', 'second', 'whiz')),
247 [(u
'first', u
'second', u
'third')]
250 # ...also works if only extra columns are included
252 list(TestObject
.objects
.extra(select
=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('foo','whiz')),
253 [(u
'first', u
'third')]
257 list(TestObject
.objects
.extra(select
=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('whiz', flat
=True)),
261 # ... and values are returned in the order they are specified
263 list(TestObject
.objects
.extra(select
=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('whiz','foo')),
264 [(u
'third', u
'first')]
268 list(TestObject
.objects
.extra(select
=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('first','id')),
273 list(TestObject
.objects
.extra(select
=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('whiz', 'first', 'bar', 'id')),
274 [(u
'third', u
'first', u
'second', obj
.pk
)]
277 def test_regression_10847(self
):
279 Regression for #10847: the list of extra columns can always be
280 accurately evaluated. Using an inner query ensures that as_sql() is
281 producing correct output without requiring full evaluation and
282 execution of the inner query.
284 obj
= TestObject(first
='first', second
='second', third
='third')
288 list(TestObject
.objects
.extra(select
={'extra': 1}).values('pk')),
292 self
.assertQuerysetEqual(
293 TestObject
.objects
.filter(
294 pk__in
=TestObject
.objects
.extra(select
={'extra': 1}).values('pk')
296 ['<TestObject: TestObject: first,second,third>']
300 list(TestObject
.objects
.values('pk').extra(select
={'extra': 1})),
304 self
.assertQuerysetEqual(
305 TestObject
.objects
.filter(
306 pk__in
=TestObject
.objects
.values('pk').extra(select
={'extra': 1})
308 ['<TestObject: TestObject: first,second,third>']
311 self
.assertQuerysetEqual(
312 TestObject
.objects
.filter(pk
=obj
.pk
) |
313 TestObject
.objects
.extra(where
=["id > %s"], params
=[obj
.pk
]),
314 ['<TestObject: TestObject: first,second,third>']