1
0
mirror of https://github.com/django/django.git synced 2024-12-23 09:36:06 +00:00
django/tests/extra_regress/tests.py
Nick Pope 847f46e9bf
Removed redundant QuerySet.all() calls in docs and tests.
Most QuerySet methods are mapped onto the Manager and, in general,
it isn't necessary to call .all() on the manager.
2022-02-22 10:29:38 +01:00

471 lines
16 KiB
Python

import datetime
from django.contrib.auth.models import User
from django.test import TestCase
from .models import Order, RevisionableModel, TestObject
class ExtraRegressTests(TestCase):
@classmethod
def setUpTestData(cls):
cls.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={"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.extra(where=["id=%s"], params=[self.u.id])
self.assertSequenceEqual(qs, [self.u])
self.assertSequenceEqual(qs[:1], [self.u])
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 optimization. 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.assertSequenceEqual(
User.objects.filter(pk=self.u.id)
.extra(select={"extra_field": 1})
.distinct(),
[self.u],
)
self.assertSequenceEqual(
User.objects.filter(pk=self.u.id).extra(
select={"extra_field": 1}, order_by=["extra_field"]
),
[self.u],
)
self.assertSequenceEqual(
User.objects.filter(pk=self.u.id)
.extra(select={"extra_field": 1}, order_by=["extra_field"])
.distinct(),
[self.u],
)
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.
"""
RevisionableModel.objects.create(
title="First Revision", when=datetime.datetime(2008, 9, 28, 10, 30, 0)
)
self.assertSequenceEqual(
RevisionableModel.objects.extra(select={"the_answer": "id"}).datetimes(
"when", "month"
),
[datetime.datetime(2008, 9, 1, 0, 0)],
)
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={"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={"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={"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={"foo": "first", "bar": "second", "whiz": "third"}
)
),
[{"second": "second", "first": "first"}],
)
# Extra columns can be partially returned
self.assertEqual(
list(
TestObject.objects.extra(
select={"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={"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={"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={"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={"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={"foo": "first", "bar": "second", "whiz": "third"}
)
),
[("first", "second")],
)
self.assertEqual(
list(
TestObject.objects.extra(
select={"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={"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={"foo": "first", "bar": "second", "whiz": "third"}
).values_list("foo", "whiz")
),
[("first", "third")],
)
self.assertEqual(
list(
TestObject.objects.extra(
select={"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={"foo": "first", "bar": "second", "whiz": "third"}
).values_list("whiz", "foo")
),
[("third", "first")],
)
self.assertEqual(
list(
TestObject.objects.extra(
select={"foo": "first", "bar": "second", "whiz": "third"}
).values_list("first", "id")
),
[("first", obj.pk)],
)
self.assertEqual(
list(
TestObject.objects.extra(
select={"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.assertSequenceEqual(
TestObject.objects.filter(
pk__in=TestObject.objects.extra(select={"extra": 1}).values("pk")
),
[obj],
)
self.assertEqual(
list(TestObject.objects.values("pk").extra(select={"extra": 1})),
[{"pk": obj.pk}],
)
self.assertSequenceEqual(
TestObject.objects.filter(
pk__in=TestObject.objects.values("pk").extra(select={"extra": 1})
),
[obj],
)
self.assertSequenceEqual(
TestObject.objects.filter(pk=obj.pk)
| TestObject.objects.extra(where=["id > %s"], params=[obj.pk]),
[obj],
)
def test_regression_17877(self):
"""
Extra WHERE clauses get correctly ANDed, even when they
contain OR operations.
"""
# Test Case 1: should appear in queryset.
t1 = TestObject.objects.create(first="a", second="a", third="a")
# Test Case 2: should appear in queryset.
t2 = TestObject.objects.create(first="b", second="a", third="a")
# 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.assertCountEqual(
TestObject.objects.extra(
where=["first = 'a' OR second = 'a'", "third = 'a'"],
),
[t1, t2],
)
def test_extra_values_distinct_ordering(self):
t1 = TestObject.objects.create(first="a", second="a", third="a")
t2 = TestObject.objects.create(first="a", second="b", third="b")
qs = (
TestObject.objects.extra(select={"second_extra": "second"})
.values_list("id", flat=True)
.distinct()
)
self.assertSequenceEqual(qs.order_by("second_extra"), [t1.pk, t2.pk])
self.assertSequenceEqual(qs.order_by("-second_extra"), [t2.pk, t1.pk])
# Note: the extra ordering must appear in select clause, so we get two
# non-distinct results here (this is on purpose, see #7070).
# Extra select doesn't appear in result values.
self.assertSequenceEqual(
qs.order_by("-second_extra").values_list("first"), [("a",), ("a",)]
)