mirror of
https://github.com/django/django.git
synced 2024-12-26 02:56:25 +00:00
e74e207cce
Thanks Carl Meyer for the review. Squashed commit of the following: commit4f290bdb60
Author: Aymeric Augustin <aymeric.augustin@m4x.org> Date: Wed Feb 13 21:21:30 2013 +0100 Used '0:00' instead of 'UTC' which doesn't always exist in Oracle. Thanks Ian Kelly for the suggestion. commit01b6366f3c
Author: Aymeric Augustin <aymeric.augustin@m4x.org> Date: Wed Feb 13 13:38:43 2013 +0100 Made tzname a parameter of datetime_extract/trunc_sql. This is required to work around a bug in Oracle. commit924a144ef8
Author: Aymeric Augustin <aymeric.augustin@m4x.org> Date: Wed Feb 13 14:47:44 2013 +0100 Added support for parameters in SELECT clauses. commitb4351d2890
Author: Aymeric Augustin <aymeric.augustin@m4x.org> Date: Mon Feb 11 22:30:22 2013 +0100 Documented backwards incompatibilities in the two previous commits. commit91ef84713c
Author: Aymeric Augustin <aymeric.augustin@m4x.org> Date: Mon Feb 11 09:42:31 2013 +0100 Used QuerySet.datetimes for the admin's date_hierarchy. commit0d0de288a5
Author: Aymeric Augustin <aymeric.augustin@m4x.org> Date: Mon Feb 11 09:29:38 2013 +0100 Used QuerySet.datetimes in date-based generic views. commit9c0859ff7c
Author: Aymeric Augustin <aymeric.augustin@m4x.org> Date: Sun Feb 10 21:43:25 2013 +0100 Implemented QuerySet.datetimes on Oracle. commit68ab511a4f
Author: Aymeric Augustin <aymeric.augustin@m4x.org> Date: Sun Feb 10 21:43:14 2013 +0100 Implemented QuerySet.datetimes on MySQL. commit22d52681d3
Author: Aymeric Augustin <aymeric.augustin@m4x.org> Date: Sun Feb 10 21:42:29 2013 +0100 Implemented QuerySet.datetimes on SQLite. commitf6800fd04c
Author: Aymeric Augustin <aymeric.augustin@m4x.org> Date: Sun Feb 10 21:43:03 2013 +0100 Implemented QuerySet.datetimes on PostgreSQL. commit0c829c23f4
Author: Aymeric Augustin <aymeric.augustin@m4x.org> Date: Sun Feb 10 21:41:08 2013 +0100 Added datetime-handling infrastructure in the ORM layers. commit104d82a777
Author: Aymeric Augustin <aymeric.augustin@m4x.org> Date: Mon Feb 11 10:05:55 2013 +0100 Updated null_queries tests to avoid clashing with the __second lookup. commitc01bbb3235
Author: Aymeric Augustin <aymeric.augustin@m4x.org> Date: Sun Feb 10 23:07:41 2013 +0100 Updated tests of .dates(). Replaced .dates() by .datetimes() for DateTimeFields. Replaced dates with datetimes in the expected output for DateFields. commit50fb7a5246
Author: Aymeric Augustin <aymeric.augustin@m4x.org> Date: Sun Feb 10 21:40:09 2013 +0100 Updated and added tests for QuerySet.datetimes. commita8451a5004
Author: Aymeric Augustin <aymeric.augustin@m4x.org> Date: Sun Feb 10 22:34:46 2013 +0100 Documented the new time lookups and updated the date lookups. commit29413eab2b
Author: Aymeric Augustin <aymeric.augustin@m4x.org> Date: Sun Feb 10 16:15:49 2013 +0100 Documented QuerySet.datetimes and updated QuerySet.dates.
351 lines
14 KiB
Python
351 lines
14 KiB
Python
from __future__ import absolute_import, unicode_literals
|
|
|
|
import datetime
|
|
|
|
from django.contrib.auth.models import User
|
|
from django.test import TestCase
|
|
from django.utils.datastructures import SortedDict
|
|
|
|
from .models import TestObject, Order, RevisionableModel
|
|
|
|
|
|
class ExtraRegressTests(TestCase):
|
|
|
|
def setUp(self):
|
|
self.u = User.objects.create_user(
|
|
username="fred",
|
|
password="secret",
|
|
email="fred@example.com"
|
|
)
|
|
|
|
def test_regression_7314_7372(self):
|
|
"""
|
|
Regression tests for #7314 and #7372
|
|
"""
|
|
rm = RevisionableModel.objects.create(
|
|
title='First Revision',
|
|
when=datetime.datetime(2008, 9, 28, 10, 30, 0)
|
|
)
|
|
self.assertEqual(rm.pk, rm.base.pk)
|
|
|
|
rm2 = rm.new_revision()
|
|
rm2.title = "Second Revision"
|
|
rm.when = datetime.datetime(2008, 9, 28, 14, 25, 0)
|
|
rm2.save()
|
|
|
|
self.assertEqual(rm2.title, 'Second Revision')
|
|
self.assertEqual(rm2.base.title, 'First Revision')
|
|
|
|
self.assertNotEqual(rm2.pk, rm.pk)
|
|
self.assertEqual(rm2.base.pk, rm.pk)
|
|
|
|
# Queryset to match most recent revision:
|
|
qs = RevisionableModel.objects.extra(
|
|
where=["%(table)s.id IN (SELECT MAX(rev.id) FROM %(table)s rev GROUP BY rev.base_id)" % {
|
|
'table': RevisionableModel._meta.db_table,
|
|
}]
|
|
)
|
|
|
|
self.assertQuerysetEqual(qs,
|
|
[('Second Revision', 'First Revision')],
|
|
transform=lambda r: (r.title, r.base.title)
|
|
)
|
|
|
|
# Queryset to search for string in title:
|
|
qs2 = RevisionableModel.objects.filter(title__contains="Revision")
|
|
self.assertQuerysetEqual(qs2,
|
|
[
|
|
('First Revision', 'First Revision'),
|
|
('Second Revision', 'First Revision'),
|
|
],
|
|
transform=lambda r: (r.title, r.base.title),
|
|
ordered=False
|
|
)
|
|
|
|
# Following queryset should return the most recent revision:
|
|
self.assertQuerysetEqual(qs & qs2,
|
|
[('Second Revision', 'First Revision')],
|
|
transform=lambda r: (r.title, r.base.title),
|
|
ordered=False
|
|
)
|
|
|
|
def test_extra_stay_tied(self):
|
|
# Extra select parameters should stay tied to their corresponding
|
|
# select portions. Applies when portions are updated or otherwise
|
|
# moved around.
|
|
qs = User.objects.extra(
|
|
select=SortedDict((("alpha", "%s"), ("beta", "2"), ("gamma", "%s"))),
|
|
select_params=(1, 3)
|
|
)
|
|
qs = qs.extra(select={"beta": 4})
|
|
qs = qs.extra(select={"alpha": "%s"}, select_params=[5])
|
|
self.assertEqual(
|
|
list(qs.filter(id=self.u.id).values('alpha', 'beta', 'gamma')),
|
|
[{'alpha': 5, 'beta': 4, 'gamma': 3}]
|
|
)
|
|
|
|
def test_regression_7957(self):
|
|
"""
|
|
Regression test for #7957: Combining extra() calls should leave the
|
|
corresponding parameters associated with the right extra() bit. I.e.
|
|
internal dictionary must remain sorted.
|
|
"""
|
|
self.assertEqual(
|
|
User.objects.extra(select={"alpha": "%s"}, select_params=(1,)
|
|
).extra(select={"beta": "%s"}, select_params=(2,))[0].alpha,
|
|
1)
|
|
|
|
self.assertEqual(
|
|
User.objects.extra(select={"beta": "%s"}, select_params=(1,)
|
|
).extra(select={"alpha": "%s"}, select_params=(2,))[0].alpha,
|
|
2)
|
|
|
|
def test_regression_7961(self):
|
|
"""
|
|
Regression test for #7961: When not using a portion of an
|
|
extra(...) in a query, remove any corresponding parameters from the
|
|
query as well.
|
|
"""
|
|
self.assertEqual(
|
|
list(User.objects.extra(select={"alpha": "%s"}, select_params=(-6,)
|
|
).filter(id=self.u.id).values_list('id', flat=True)),
|
|
[self.u.id]
|
|
)
|
|
|
|
def test_regression_8063(self):
|
|
"""
|
|
Regression test for #8063: limiting a query shouldn't discard any
|
|
extra() bits.
|
|
"""
|
|
qs = User.objects.all().extra(where=['id=%s'], params=[self.u.id])
|
|
self.assertQuerysetEqual(qs, ['<User: fred>'])
|
|
self.assertQuerysetEqual(qs[:1], ['<User: fred>'])
|
|
|
|
def test_regression_8039(self):
|
|
"""
|
|
Regression test for #8039: Ordering sometimes removed relevant tables
|
|
from extra(). This test is the critical case: ordering uses a table,
|
|
but then removes the reference because of an optimisation. The table
|
|
should still be present because of the extra() call.
|
|
"""
|
|
self.assertQuerysetEqual(
|
|
Order.objects.extra(where=["username=%s"],
|
|
params=["fred"],
|
|
tables=["auth_user"]
|
|
).order_by('created_by'),
|
|
[]
|
|
)
|
|
|
|
def test_regression_8819(self):
|
|
"""
|
|
Regression test for #8819: Fields in the extra(select=...) list
|
|
should be available to extra(order_by=...).
|
|
"""
|
|
self.assertQuerysetEqual(
|
|
User.objects.filter(pk=self.u.id).extra(select={'extra_field': 1}).distinct(),
|
|
['<User: fred>']
|
|
)
|
|
self.assertQuerysetEqual(
|
|
User.objects.filter(pk=self.u.id).extra(select={'extra_field': 1}, order_by=['extra_field']),
|
|
['<User: fred>']
|
|
)
|
|
self.assertQuerysetEqual(
|
|
User.objects.filter(pk=self.u.id).extra(select={'extra_field': 1}, order_by=['extra_field']).distinct(),
|
|
['<User: fred>']
|
|
)
|
|
|
|
def test_dates_query(self):
|
|
"""
|
|
When calling the dates() method on a queryset with extra selection
|
|
columns, we can (and should) ignore those columns. They don't change
|
|
the result and cause incorrect SQL to be produced otherwise.
|
|
"""
|
|
rm = RevisionableModel.objects.create(
|
|
title='First Revision',
|
|
when=datetime.datetime(2008, 9, 28, 10, 30, 0)
|
|
)
|
|
|
|
self.assertQuerysetEqual(
|
|
RevisionableModel.objects.extra(select={"the_answer": 'id'}).datetimes('when', 'month'),
|
|
[datetime.datetime(2008, 9, 1, 0, 0)],
|
|
transform=lambda d: d,
|
|
)
|
|
|
|
def test_values_with_extra(self):
|
|
"""
|
|
Regression test for #10256... If there is a values() clause, Extra
|
|
columns are only returned if they are explicitly mentioned.
|
|
"""
|
|
obj = TestObject(first='first', second='second', third='third')
|
|
obj.save()
|
|
|
|
self.assertEqual(
|
|
list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values()),
|
|
[{'bar': 'second', 'third': 'third', 'second': 'second', 'whiz': 'third', 'foo': 'first', 'id': obj.pk, 'first': 'first'}]
|
|
)
|
|
|
|
# Extra clauses after an empty values clause are still included
|
|
self.assertEqual(
|
|
list(TestObject.objects.values().extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third'))))),
|
|
[{'bar': 'second', 'third': 'third', 'second': 'second', 'whiz': 'third', 'foo': 'first', 'id': obj.pk, 'first': 'first'}]
|
|
)
|
|
|
|
# Extra columns are ignored if not mentioned in the values() clause
|
|
self.assertEqual(
|
|
list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values('first', 'second')),
|
|
[{'second': 'second', 'first': 'first'}]
|
|
)
|
|
|
|
# Extra columns after a non-empty values() clause are ignored
|
|
self.assertEqual(
|
|
list(TestObject.objects.values('first', 'second').extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third'))))),
|
|
[{'second': 'second', 'first': 'first'}]
|
|
)
|
|
|
|
# Extra columns can be partially returned
|
|
self.assertEqual(
|
|
list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values('first', 'second', 'foo')),
|
|
[{'second': 'second', 'foo': 'first', 'first': 'first'}]
|
|
)
|
|
|
|
# Also works if only extra columns are included
|
|
self.assertEqual(
|
|
list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values('foo', 'whiz')),
|
|
[{'foo': 'first', 'whiz': 'third'}]
|
|
)
|
|
|
|
# Values list works the same way
|
|
# All columns are returned for an empty values_list()
|
|
self.assertEqual(
|
|
list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list()),
|
|
[('first', 'second', 'third', obj.pk, 'first', 'second', 'third')]
|
|
)
|
|
|
|
# Extra columns after an empty values_list() are still included
|
|
self.assertEqual(
|
|
list(TestObject.objects.values_list().extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third'))))),
|
|
[('first', 'second', 'third', obj.pk, 'first', 'second', 'third')]
|
|
)
|
|
|
|
# Extra columns ignored completely if not mentioned in values_list()
|
|
self.assertEqual(
|
|
list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('first', 'second')),
|
|
[('first', 'second')]
|
|
)
|
|
|
|
# Extra columns after a non-empty values_list() clause are ignored completely
|
|
self.assertEqual(
|
|
list(TestObject.objects.values_list('first', 'second').extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third'))))),
|
|
[('first', 'second')]
|
|
)
|
|
|
|
self.assertEqual(
|
|
list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('second', flat=True)),
|
|
['second']
|
|
)
|
|
|
|
# Only the extra columns specified in the values_list() are returned
|
|
self.assertEqual(
|
|
list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('first', 'second', 'whiz')),
|
|
[('first', 'second', 'third')]
|
|
)
|
|
|
|
# ...also works if only extra columns are included
|
|
self.assertEqual(
|
|
list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('foo','whiz')),
|
|
[('first', 'third')]
|
|
)
|
|
|
|
self.assertEqual(
|
|
list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('whiz', flat=True)),
|
|
['third']
|
|
)
|
|
|
|
# ... and values are returned in the order they are specified
|
|
self.assertEqual(
|
|
list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('whiz','foo')),
|
|
[('third', 'first')]
|
|
)
|
|
|
|
self.assertEqual(
|
|
list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('first','id')),
|
|
[('first', obj.pk)]
|
|
)
|
|
|
|
self.assertEqual(
|
|
list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('whiz', 'first', 'bar', 'id')),
|
|
[('third', 'first', 'second', obj.pk)]
|
|
)
|
|
|
|
def test_regression_10847(self):
|
|
"""
|
|
Regression for #10847: the list of extra columns can always be
|
|
accurately evaluated. Using an inner query ensures that as_sql() is
|
|
producing correct output without requiring full evaluation and
|
|
execution of the inner query.
|
|
"""
|
|
obj = TestObject(first='first', second='second', third='third')
|
|
obj.save()
|
|
|
|
self.assertEqual(
|
|
list(TestObject.objects.extra(select={'extra': 1}).values('pk')),
|
|
[{'pk': obj.pk}]
|
|
)
|
|
|
|
self.assertQuerysetEqual(
|
|
TestObject.objects.filter(
|
|
pk__in=TestObject.objects.extra(select={'extra': 1}).values('pk')
|
|
),
|
|
['<TestObject: TestObject: first,second,third>']
|
|
)
|
|
|
|
self.assertEqual(
|
|
list(TestObject.objects.values('pk').extra(select={'extra': 1})),
|
|
[{'pk': obj.pk}]
|
|
)
|
|
|
|
self.assertQuerysetEqual(
|
|
TestObject.objects.filter(
|
|
pk__in=TestObject.objects.values('pk').extra(select={'extra': 1})
|
|
),
|
|
['<TestObject: TestObject: first,second,third>']
|
|
)
|
|
|
|
self.assertQuerysetEqual(
|
|
TestObject.objects.filter(pk=obj.pk) |
|
|
TestObject.objects.extra(where=["id > %s"], params=[obj.pk]),
|
|
['<TestObject: TestObject: first,second,third>']
|
|
)
|
|
|
|
def test_regression_17877(self):
|
|
"""
|
|
Ensure that extra WHERE clauses get correctly ANDed, even when they
|
|
contain OR operations.
|
|
"""
|
|
# Test Case 1: should appear in queryset.
|
|
t = TestObject(first='a', second='a', third='a')
|
|
t.save()
|
|
# Test Case 2: should appear in queryset.
|
|
t = TestObject(first='b', second='a', third='a')
|
|
t.save()
|
|
# Test Case 3: should not appear in queryset, bug case.
|
|
t = TestObject(first='a', second='a', third='b')
|
|
t.save()
|
|
# Test Case 4: should not appear in queryset.
|
|
t = TestObject(first='b', second='a', third='b')
|
|
t.save()
|
|
# Test Case 5: should not appear in queryset.
|
|
t = TestObject(first='b', second='b', third='a')
|
|
t.save()
|
|
# Test Case 6: should not appear in queryset, bug case.
|
|
t = TestObject(first='a', second='b', third='b')
|
|
t.save()
|
|
|
|
self.assertQuerysetEqual(
|
|
TestObject.objects.extra(
|
|
where=["first = 'a' OR second = 'a'", "third = 'a'"],
|
|
),
|
|
['<TestObject: TestObject: a,a,a>', '<TestObject: TestObject: b,a,a>'],
|
|
ordered=False
|
|
)
|