Fixed #27995 -- Added error messages on unsupported operations following union(), intersection(), and difference().
This commit is contained in:
parent
f13147c8de
commit
1853383969
|
@ -401,7 +401,7 @@ class QuerySet:
|
|||
Perform the query and return a single object matching the given
|
||||
keyword arguments.
|
||||
"""
|
||||
clone = self.filter(*args, **kwargs)
|
||||
clone = self._chain() if self.query.combinator else self.filter(*args, **kwargs)
|
||||
if self.query.can_filter() and not self.query.distinct_fields:
|
||||
clone = clone.order_by()
|
||||
limit = None
|
||||
|
@ -890,6 +890,7 @@ class QuerySet:
|
|||
Return a new QuerySet instance with the args ANDed to the existing
|
||||
set.
|
||||
"""
|
||||
self._not_support_combined_queries('filter')
|
||||
return self._filter_or_exclude(False, *args, **kwargs)
|
||||
|
||||
def exclude(self, *args, **kwargs):
|
||||
|
@ -897,6 +898,7 @@ class QuerySet:
|
|||
Return a new QuerySet instance with NOT (args) ANDed to the existing
|
||||
set.
|
||||
"""
|
||||
self._not_support_combined_queries('exclude')
|
||||
return self._filter_or_exclude(True, *args, **kwargs)
|
||||
|
||||
def _filter_or_exclude(self, negate, *args, **kwargs):
|
||||
|
@ -985,7 +987,7 @@ class QuerySet:
|
|||
|
||||
If select_related(None) is called, clear the list.
|
||||
"""
|
||||
|
||||
self._not_support_combined_queries('select_related')
|
||||
if self._fields is not None:
|
||||
raise TypeError("Cannot call select_related() after .values() or .values_list()")
|
||||
|
||||
|
@ -1007,6 +1009,7 @@ class QuerySet:
|
|||
When prefetch_related() is called more than once, append to the list of
|
||||
prefetch lookups. If prefetch_related(None) is called, clear the list.
|
||||
"""
|
||||
self._not_support_combined_queries('prefetch_related')
|
||||
clone = self._chain()
|
||||
if lookups == (None,):
|
||||
clone._prefetch_related_lookups = ()
|
||||
|
@ -1025,6 +1028,7 @@ class QuerySet:
|
|||
Return a query set in which the returned objects have been annotated
|
||||
with extra data or aggregations.
|
||||
"""
|
||||
self._not_support_combined_queries('annotate')
|
||||
self._validate_values_are_expressions(args + tuple(kwargs.values()), method_name='annotate')
|
||||
annotations = {}
|
||||
for arg in args:
|
||||
|
@ -1088,6 +1092,7 @@ class QuerySet:
|
|||
def extra(self, select=None, where=None, params=None, tables=None,
|
||||
order_by=None, select_params=None):
|
||||
"""Add extra SQL fragments to the query."""
|
||||
self._not_support_combined_queries('extra')
|
||||
assert self.query.can_filter(), \
|
||||
"Cannot change a query once a slice has been taken"
|
||||
clone = self._chain()
|
||||
|
@ -1109,6 +1114,7 @@ class QuerySet:
|
|||
The only exception to this is if None is passed in as the only
|
||||
parameter, in which case removal all deferrals.
|
||||
"""
|
||||
self._not_support_combined_queries('defer')
|
||||
if self._fields is not None:
|
||||
raise TypeError("Cannot call defer() after .values() or .values_list()")
|
||||
clone = self._chain()
|
||||
|
@ -1124,6 +1130,7 @@ class QuerySet:
|
|||
method and that are not already specified as deferred are loaded
|
||||
immediately when the queryset is evaluated.
|
||||
"""
|
||||
self._not_support_combined_queries('only')
|
||||
if self._fields is not None:
|
||||
raise TypeError("Cannot call only() after .values() or .values_list()")
|
||||
if fields == (None,):
|
||||
|
@ -1312,6 +1319,13 @@ class QuerySet:
|
|||
)
|
||||
)
|
||||
|
||||
def _not_support_combined_queries(self, operation_name):
|
||||
if self.query.combinator:
|
||||
raise NotSupportedError(
|
||||
'Calling QuerySet.%s() after %s() is not supported.'
|
||||
% (operation_name, self.query.combinator)
|
||||
)
|
||||
|
||||
|
||||
class InstanceCheckMeta(type):
|
||||
def __instancecheck__(self, instance):
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
from django.db import connection
|
||||
from django.db.models import Exists, F, IntegerField, OuterRef, Value
|
||||
from django.db.utils import DatabaseError, NotSupportedError
|
||||
from django.test import TestCase, skipIfDBFeature, skipUnlessDBFeature
|
||||
|
@ -258,3 +259,29 @@ class QuerySetSetOperationTests(TestCase):
|
|||
numbers = list(range(10))
|
||||
self.assertNumbersEqual(union.order_by('num'), numbers)
|
||||
self.assertNumbersEqual(union.order_by('other_num'), reversed(numbers))
|
||||
|
||||
def test_unsupported_operations_on_combined_qs(self):
|
||||
qs = Number.objects.all()
|
||||
msg = 'Calling QuerySet.%s() after %s() is not supported.'
|
||||
combinators = ['union']
|
||||
if connection.features.supports_select_difference:
|
||||
combinators.append('difference')
|
||||
if connection.features.supports_select_intersection:
|
||||
combinators.append('intersection')
|
||||
for combinator in combinators:
|
||||
for operation in (
|
||||
'annotate',
|
||||
'defer',
|
||||
'exclude',
|
||||
'extra',
|
||||
'filter',
|
||||
'only',
|
||||
'prefetch_related',
|
||||
'select_related',
|
||||
):
|
||||
with self.subTest(combinator=combinator, operation=operation):
|
||||
with self.assertRaisesMessage(
|
||||
NotSupportedError,
|
||||
msg % (operation, combinator),
|
||||
):
|
||||
getattr(getattr(qs, combinator)(qs), operation)()
|
||||
|
|
Loading…
Reference in New Issue