Added Query.is_sliced property.

Previously, we used Query.can_filter() mainly to check if a query is
sliced what was confusing.
This commit is contained in:
Mariusz Felisiak 2019-07-25 20:45:55 +02:00 committed by GitHub
parent 1853383969
commit 806ba19bbf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 18 additions and 15 deletions

View File

@ -635,7 +635,7 @@ class QuerySet:
"arguments or 'get_latest_by' in the model's Meta." "arguments or 'get_latest_by' in the model's Meta."
) )
assert self.query.can_filter(), \ assert not self.query.is_sliced, \
"Cannot change a query once a slice has been taken." "Cannot change a query once a slice has been taken."
obj = self._chain() obj = self._chain()
obj.query.set_limits(high=1) obj.query.set_limits(high=1)
@ -664,7 +664,7 @@ class QuerySet:
Return a dictionary mapping each of the given IDs to the object with Return a dictionary mapping each of the given IDs to the object with
that ID. If `id_list` isn't provided, evaluate the entire QuerySet. that ID. If `id_list` isn't provided, evaluate the entire QuerySet.
""" """
assert self.query.can_filter(), \ assert not self.query.is_sliced, \
"Cannot use 'limit' or 'offset' with in_bulk" "Cannot use 'limit' or 'offset' with in_bulk"
if field_name != 'pk' and not self.model._meta.get_field(field_name).unique: if field_name != 'pk' and not self.model._meta.get_field(field_name).unique:
raise ValueError("in_bulk()'s field_name must be a unique field but %r isn't." % field_name) raise ValueError("in_bulk()'s field_name must be a unique field but %r isn't." % field_name)
@ -689,7 +689,7 @@ class QuerySet:
def delete(self): def delete(self):
"""Delete the records in the current QuerySet.""" """Delete the records in the current QuerySet."""
assert self.query.can_filter(), \ assert not self.query.is_sliced, \
"Cannot use 'limit' or 'offset' with delete." "Cannot use 'limit' or 'offset' with delete."
if self._fields is not None: if self._fields is not None:
@ -731,7 +731,7 @@ class QuerySet:
Update all elements in the current QuerySet, setting all the given Update all elements in the current QuerySet, setting all the given
fields to the appropriate values. fields to the appropriate values.
""" """
assert self.query.can_filter(), \ assert not self.query.is_sliced, \
"Cannot update a query once a slice has been taken." "Cannot update a query once a slice has been taken."
self._for_write = True self._for_write = True
query = self.query.chain(sql.UpdateQuery) query = self.query.chain(sql.UpdateQuery)
@ -751,7 +751,7 @@ class QuerySet:
code (it requires too much poking around at model internals to be code (it requires too much poking around at model internals to be
useful at that level). useful at that level).
""" """
assert self.query.can_filter(), \ assert not self.query.is_sliced, \
"Cannot update a query once a slice has been taken." "Cannot update a query once a slice has been taken."
query = self.query.chain(sql.UpdateQuery) query = self.query.chain(sql.UpdateQuery)
query.add_update_fields(values) query.add_update_fields(values)
@ -903,7 +903,7 @@ class QuerySet:
def _filter_or_exclude(self, negate, *args, **kwargs): def _filter_or_exclude(self, negate, *args, **kwargs):
if args or kwargs: if args or kwargs:
assert self.query.can_filter(), \ assert not self.query.is_sliced, \
"Cannot filter a query once a slice has been taken." "Cannot filter a query once a slice has been taken."
clone = self._chain() clone = self._chain()
@ -1072,7 +1072,7 @@ class QuerySet:
def order_by(self, *field_names): def order_by(self, *field_names):
"""Return a new QuerySet instance with the ordering changed.""" """Return a new QuerySet instance with the ordering changed."""
assert self.query.can_filter(), \ assert not self.query.is_sliced, \
"Cannot reorder a query once a slice has been taken." "Cannot reorder a query once a slice has been taken."
obj = self._chain() obj = self._chain()
obj.query.clear_ordering(force_empty=False) obj.query.clear_ordering(force_empty=False)
@ -1083,7 +1083,7 @@ class QuerySet:
""" """
Return a new QuerySet instance that will select only distinct results. Return a new QuerySet instance that will select only distinct results.
""" """
assert self.query.can_filter(), \ assert not self.query.is_sliced, \
"Cannot create distinct fields once a slice has been taken." "Cannot create distinct fields once a slice has been taken."
obj = self._chain() obj = self._chain()
obj.query.add_distinct_fields(*field_names) obj.query.add_distinct_fields(*field_names)
@ -1093,7 +1093,7 @@ class QuerySet:
order_by=None, select_params=None): order_by=None, select_params=None):
"""Add extra SQL fragments to the query.""" """Add extra SQL fragments to the query."""
self._not_support_combined_queries('extra') self._not_support_combined_queries('extra')
assert self.query.can_filter(), \ assert not self.query.is_sliced, \
"Cannot change a query once a slice has been taken" "Cannot change a query once a slice has been taken"
clone = self._chain() clone = self._chain()
clone.query.add_extra(select, select_params, where, params, tables, order_by) clone.query.add_extra(select, select_params, where, params, tables, order_by)
@ -1101,7 +1101,7 @@ class QuerySet:
def reverse(self): def reverse(self):
"""Reverse the ordering of the QuerySet.""" """Reverse the ordering of the QuerySet."""
if not self.query.can_filter(): if self.query.is_sliced:
raise TypeError('Cannot reverse a query once a slice has been taken.') raise TypeError('Cannot reverse a query once a slice has been taken.')
clone = self._chain() clone = self._chain()
clone.query.standard_ordering = not clone.query.standard_ordering clone.query.standard_ordering = not clone.query.standard_ordering

View File

@ -413,7 +413,6 @@ class Query(BaseExpression):
""" """
if not self.annotation_select: if not self.annotation_select:
return {} return {}
has_limit = self.low_mark != 0 or self.high_mark is not None
existing_annotations = [ existing_annotations = [
annotation for alias, annotation annotation for alias, annotation
in self.annotations.items() in self.annotations.items()
@ -430,7 +429,7 @@ class Query(BaseExpression):
# those operations must be done in a subquery so that the query # those operations must be done in a subquery so that the query
# aggregates on the limit and/or distinct results instead of applying # aggregates on the limit and/or distinct results instead of applying
# the distinct and limit after the aggregation. # the distinct and limit after the aggregation.
if (isinstance(self.group_by, tuple) or has_limit or existing_annotations or if (isinstance(self.group_by, tuple) or self.is_sliced or existing_annotations or
self.distinct or self.combinator): self.distinct or self.combinator):
from django.db.models.sql.subqueries import AggregateQuery from django.db.models.sql.subqueries import AggregateQuery
outer_query = AggregateQuery(self.model) outer_query = AggregateQuery(self.model)
@ -438,7 +437,7 @@ class Query(BaseExpression):
inner_query.select_for_update = False inner_query.select_for_update = False
inner_query.select_related = False inner_query.select_related = False
inner_query.set_annotation_mask(self.annotation_select) inner_query.set_annotation_mask(self.annotation_select)
if not has_limit and not self.distinct_fields: if not self.is_sliced and not self.distinct_fields:
# Queries with distinct_fields need ordering and when a limit # Queries with distinct_fields need ordering and when a limit
# is applied we must take the slice from the ordered query. # is applied we must take the slice from the ordered query.
# Otherwise no need for ordering. # Otherwise no need for ordering.
@ -548,7 +547,7 @@ class Query(BaseExpression):
""" """
assert self.model == rhs.model, \ assert self.model == rhs.model, \
"Cannot combine queries on two different base models." "Cannot combine queries on two different base models."
assert self.can_filter(), \ assert not self.is_sliced, \
"Cannot combine queries once a slice has been taken." "Cannot combine queries once a slice has been taken."
assert self.distinct == rhs.distinct, \ assert self.distinct == rhs.distinct, \
"Cannot combine a unique query with a non-unique query." "Cannot combine a unique query with a non-unique query."
@ -1762,6 +1761,10 @@ class Query(BaseExpression):
"""Clear any existing limits.""" """Clear any existing limits."""
self.low_mark, self.high_mark = 0, None self.low_mark, self.high_mark = 0, None
@property
def is_sliced(self):
return self.low_mark != 0 or self.high_mark is not None
def has_limit_one(self): def has_limit_one(self):
return self.high_mark is not None and (self.high_mark - self.low_mark) == 1 return self.high_mark is not None and (self.high_mark - self.low_mark) == 1
@ -1771,7 +1774,7 @@ class Query(BaseExpression):
Typically, this means no limits or offsets have been put on the results. Typically, this means no limits or offsets have been put on the results.
""" """
return not self.low_mark and self.high_mark is None return not self.is_sliced
def clear_select_clause(self): def clear_select_clause(self):
"""Remove all fields from SELECT clause.""" """Remove all fields from SELECT clause."""