mirror of
https://github.com/django/django.git
synced 2025-11-18 11:00:24 +00:00
Fix composed queries keeping columns after multiple values()/values_list() calls
Allow changing the list of columns in composed queries (union, intersection, difference) when values() or values_list() is called multiple times on the same queryset, ensuring correct results in repeated evaluations. Closes issue in compiler.py#L428-L433. Adds regression tests to cover this behavior.
This commit is contained in:
parent
a7038adbd0
commit
feaa06d96d
2 changed files with 70 additions and 2 deletions
|
|
@ -424,8 +424,18 @@ class SQLCompiler:
|
|||
try:
|
||||
# If the columns list is limited, then all combined queries
|
||||
# must have the same columns list. Set the selects defined on
|
||||
# the query on all combined queries, if not already set.
|
||||
if not compiler.query.values_select and self.query.values_select:
|
||||
# the query on all combined queries. Update combined queries
|
||||
# if the values_select differs to allow values()/values_list()
|
||||
# to be called multiple times on the combined query, but only
|
||||
# if both queries use the same model.
|
||||
if (
|
||||
self.query.values_select and
|
||||
compiler.query.model == self.query.model and
|
||||
(
|
||||
not compiler.query.values_select or
|
||||
compiler.query.values_select != self.query.values_select
|
||||
)
|
||||
):
|
||||
compiler.query.set_values((
|
||||
*self.query.extra_select,
|
||||
*self.query.values_select,
|
||||
|
|
|
|||
|
|
@ -124,6 +124,64 @@ class QuerySetSetOperationTests(TestCase):
|
|||
reserved_name = qs1.union(qs1).values_list('name', 'order', 'id').get()
|
||||
self.assertEqual(reserved_name[:2], ('a', 2))
|
||||
|
||||
def test_union_multiple_values_list(self):
|
||||
# Test that calling values_list() multiple times on the same
|
||||
# composed query properly changes the columns.
|
||||
ReservedName.objects.create(name='a', order=2)
|
||||
qs1 = ReservedName.objects.all()
|
||||
union = qs1.union(qs1)
|
||||
# First call with two fields
|
||||
result1 = union.values_list('name', 'order').get()
|
||||
self.assertEqual(result1, ('a', 2))
|
||||
# Second call with different fields - should return only one field
|
||||
result2 = union.values_list('order').get()
|
||||
self.assertEqual(result2, (2,))
|
||||
# Third call with different fields
|
||||
result3 = union.values_list('name').get()
|
||||
self.assertEqual(result3, ('a',))
|
||||
|
||||
@skipUnlessDBFeature('supports_select_intersection')
|
||||
def test_intersection_multiple_values_list(self):
|
||||
# Test that calling values_list() multiple times on the same
|
||||
# composed query properly changes the columns.
|
||||
ReservedName.objects.create(name='b', order=3)
|
||||
qs1 = ReservedName.objects.all()
|
||||
intersection = qs1.intersection(qs1)
|
||||
# First call with two fields
|
||||
result1 = intersection.values_list('name', 'order').get()
|
||||
self.assertEqual(result1, ('b', 3))
|
||||
# Second call with different fields - should return only one field
|
||||
result2 = intersection.values_list('order').get()
|
||||
self.assertEqual(result2, (3,))
|
||||
|
||||
@skipUnlessDBFeature('supports_select_difference')
|
||||
def test_difference_multiple_values_list(self):
|
||||
# Test that calling values_list() multiple times on the same
|
||||
# composed query properly changes the columns.
|
||||
ReservedName.objects.create(name='c', order=4)
|
||||
qs1 = ReservedName.objects.all()
|
||||
qs2 = ReservedName.objects.none()
|
||||
difference = qs1.difference(qs2)
|
||||
# First call with two fields
|
||||
result1 = difference.values_list('name', 'order').get()
|
||||
self.assertEqual(result1, ('c', 4))
|
||||
# Second call with different fields - should return only one field
|
||||
result2 = difference.values_list('order').get()
|
||||
self.assertEqual(result2, (4,))
|
||||
|
||||
def test_union_multiple_values(self):
|
||||
# Test that calling values() multiple times on the same
|
||||
# composed query properly changes the columns.
|
||||
ReservedName.objects.create(name='d', order=5)
|
||||
qs1 = ReservedName.objects.all()
|
||||
union = qs1.union(qs1)
|
||||
# First call with two fields
|
||||
result1 = union.values('name', 'order').get()
|
||||
self.assertEqual(result1, {'name': 'd', 'order': 5})
|
||||
# Second call with different fields - should return only one field
|
||||
result2 = union.values('order').get()
|
||||
self.assertEqual(result2, {'order': 5})
|
||||
|
||||
def test_union_with_two_annotated_values_list(self):
|
||||
qs1 = Number.objects.filter(num=1).annotate(
|
||||
count=Value(0, IntegerField()),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue