mirror of
https://github.com/django/django.git
synced 2025-07-01 08:19:19 +00:00
Deprecated QuerySet.extra() except for the tables option.
This commit is contained in:
parent
5d539daa56
commit
9a83c66561
@ -20,10 +20,17 @@ from django.db import (
|
|||||||
router,
|
router,
|
||||||
transaction,
|
transaction,
|
||||||
)
|
)
|
||||||
from django.db.models import AutoField, DateField, DateTimeField, Field, sql
|
from django.db.models import (
|
||||||
|
AutoField,
|
||||||
|
BooleanField,
|
||||||
|
DateField,
|
||||||
|
DateTimeField,
|
||||||
|
Field,
|
||||||
|
sql,
|
||||||
|
)
|
||||||
from django.db.models.constants import LOOKUP_SEP, OnConflict
|
from django.db.models.constants import LOOKUP_SEP, OnConflict
|
||||||
from django.db.models.deletion import Collector
|
from django.db.models.deletion import Collector
|
||||||
from django.db.models.expressions import Case, F, Value, When
|
from django.db.models.expressions import Case, Col, F, OrderBy, RawSQL, Value, When
|
||||||
from django.db.models.functions import Cast, Trunc
|
from django.db.models.functions import Cast, Trunc
|
||||||
from django.db.models.query_utils import FilteredRelation, Q
|
from django.db.models.query_utils import FilteredRelation, Q
|
||||||
from django.db.models.sql.constants import CURSOR, GET_ITERATOR_CHUNK_SIZE
|
from django.db.models.sql.constants import CURSOR, GET_ITERATOR_CHUNK_SIZE
|
||||||
@ -203,9 +210,7 @@ class ValuesIterable(BaseIterable):
|
|||||||
if query.selected:
|
if query.selected:
|
||||||
names = list(query.selected)
|
names = list(query.selected)
|
||||||
else:
|
else:
|
||||||
# extra(select=...) cols are always at the start of the row.
|
|
||||||
names = [
|
names = [
|
||||||
*query.extra_select,
|
|
||||||
*query.values_select,
|
*query.values_select,
|
||||||
*query.annotation_select,
|
*query.annotation_select,
|
||||||
]
|
]
|
||||||
@ -246,7 +251,6 @@ class NamedValuesListIterable(ValuesListIterable):
|
|||||||
else:
|
else:
|
||||||
query = queryset.query
|
query = queryset.query
|
||||||
names = [
|
names = [
|
||||||
*query.extra_select,
|
|
||||||
*query.values_select,
|
*query.values_select,
|
||||||
*query.annotation_select,
|
*query.annotation_select,
|
||||||
]
|
]
|
||||||
@ -1711,7 +1715,68 @@ class QuerySet(AltersData):
|
|||||||
if self.query.is_sliced:
|
if self.query.is_sliced:
|
||||||
raise TypeError("Cannot change a query once a slice has been taken.")
|
raise TypeError("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)
|
if tables:
|
||||||
|
clone.query.add_extra_tables(tables)
|
||||||
|
if select and (not clone.query.values_select or clone.query.values_select_all):
|
||||||
|
warnings.warn(
|
||||||
|
"extra(select) usage is deprecated, use annotate() with RawSQL "
|
||||||
|
"instead.",
|
||||||
|
category=RemovedInDjango60Warning,
|
||||||
|
stacklevel=2,
|
||||||
|
)
|
||||||
|
# XXX: This is mising the logic to always place extras at the begining
|
||||||
|
# of the select clause.
|
||||||
|
if select_params:
|
||||||
|
param_iter = iter(select_params)
|
||||||
|
else:
|
||||||
|
param_iter = iter([])
|
||||||
|
for name, entry in select.items():
|
||||||
|
self.query.check_alias(name)
|
||||||
|
entry = str(entry)
|
||||||
|
entry_params = []
|
||||||
|
pos = entry.find("%s")
|
||||||
|
while pos != -1:
|
||||||
|
if pos == 0 or entry[pos - 1] != "%":
|
||||||
|
entry_params.append(next(param_iter))
|
||||||
|
pos = entry.find("%s", pos + 2)
|
||||||
|
clone.query.add_annotation(RawSQL(entry, entry_params), name)
|
||||||
|
if where:
|
||||||
|
warnings.warn(
|
||||||
|
"extra(where) usage is deprecated, use filter() with RawSQL instead.",
|
||||||
|
category=RemovedInDjango60Warning,
|
||||||
|
stacklevel=2,
|
||||||
|
)
|
||||||
|
clone = clone.filter(
|
||||||
|
RawSQL(
|
||||||
|
" AND ".join(f"({w})" for w in where), params or (), BooleanField()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if order_by:
|
||||||
|
warnings.warn(
|
||||||
|
"extra(order_by) usage is deprecated, use order_by() instead.",
|
||||||
|
category=RemovedInDjango60Warning,
|
||||||
|
stacklevel=2,
|
||||||
|
)
|
||||||
|
order_by_exprs = []
|
||||||
|
for order_sql in order_by:
|
||||||
|
descending = False
|
||||||
|
if order_sql.startswith("-"):
|
||||||
|
order_sql = order_sql[1:]
|
||||||
|
descending = True
|
||||||
|
if "." in order_sql:
|
||||||
|
alias, column = order_sql.split(".", 1)
|
||||||
|
target = Field(db_column=column)
|
||||||
|
target.set_attributes_from_name(name=None)
|
||||||
|
order_expr = Col(alias, target)
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
clone.query.names_to_path([order_sql], self.model._meta)
|
||||||
|
except exceptions.FieldError:
|
||||||
|
order_expr = RawSQL(order_sql, ())
|
||||||
|
else:
|
||||||
|
order_expr = F(order_sql)
|
||||||
|
order_by_exprs.append(OrderBy(order_expr, descending))
|
||||||
|
clone = clone.order_by(*order_by_exprs)
|
||||||
return clone
|
return clone
|
||||||
|
|
||||||
def reverse(self):
|
def reverse(self):
|
||||||
@ -1778,7 +1843,7 @@ class QuerySet(AltersData):
|
|||||||
"""
|
"""
|
||||||
if isinstance(self, EmptyQuerySet):
|
if isinstance(self, EmptyQuerySet):
|
||||||
return True
|
return True
|
||||||
if self.query.extra_order_by or self.query.order_by:
|
if self.query.order_by:
|
||||||
return True
|
return True
|
||||||
elif (
|
elif (
|
||||||
self.query.default_ordering
|
self.query.default_ordering
|
||||||
@ -1930,7 +1995,6 @@ class QuerySet(AltersData):
|
|||||||
"""Check that two QuerySet classes may be merged."""
|
"""Check that two QuerySet classes may be merged."""
|
||||||
if self._fields is not None and (
|
if self._fields is not None and (
|
||||||
set(self.query.values_select) != set(other.query.values_select)
|
set(self.query.values_select) != set(other.query.values_select)
|
||||||
or set(self.query.extra_select) != set(other.query.extra_select)
|
|
||||||
or set(self.query.annotation_select) != set(other.query.annotation_select)
|
or set(self.query.annotation_select) != set(other.query.annotation_select)
|
||||||
):
|
):
|
||||||
raise TypeError(
|
raise TypeError(
|
||||||
|
@ -258,20 +258,11 @@ class SQLCompiler:
|
|||||||
if cols:
|
if cols:
|
||||||
klass_info = {
|
klass_info = {
|
||||||
"model": self.query.model,
|
"model": self.query.model,
|
||||||
"select_fields": list(
|
"select_fields": list(range(0, len(cols))),
|
||||||
range(
|
|
||||||
len(self.query.extra_select),
|
|
||||||
len(self.query.extra_select) + len(cols),
|
|
||||||
)
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
selected = []
|
selected = []
|
||||||
if self.query.selected is None:
|
if self.query.selected is None:
|
||||||
selected = [
|
selected = [
|
||||||
*(
|
|
||||||
(alias, RawSQL(*args))
|
|
||||||
for alias, args in self.query.extra_select.items()
|
|
||||||
),
|
|
||||||
*((None, col) for col in cols),
|
*((None, col) for col in cols),
|
||||||
*self.query.annotation_select.items(),
|
*self.query.annotation_select.items(),
|
||||||
]
|
]
|
||||||
@ -329,9 +320,7 @@ class SQLCompiler:
|
|||||||
return ret, klass_info, annotations
|
return ret, klass_info, annotations
|
||||||
|
|
||||||
def _order_by_pairs(self):
|
def _order_by_pairs(self):
|
||||||
if self.query.extra_order_by:
|
if not self.query.default_ordering:
|
||||||
ordering = self.query.extra_order_by
|
|
||||||
elif not self.query.default_ordering:
|
|
||||||
ordering = self.query.order_by
|
ordering = self.query.order_by
|
||||||
elif self.query.order_by:
|
elif self.query.order_by:
|
||||||
ordering = self.query.order_by
|
ordering = self.query.order_by
|
||||||
@ -428,35 +417,6 @@ class SQLCompiler:
|
|||||||
yield OrderBy(expr, descending=descending), False
|
yield OrderBy(expr, descending=descending), False
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if "." in field:
|
|
||||||
# This came in through an extra(order_by=...) addition. Pass it
|
|
||||||
# on verbatim.
|
|
||||||
table, col = col.split(".", 1)
|
|
||||||
yield (
|
|
||||||
OrderBy(
|
|
||||||
RawSQL(
|
|
||||||
"%s.%s" % (self.quote_name_unless_alias(table), col), []
|
|
||||||
),
|
|
||||||
descending=descending,
|
|
||||||
),
|
|
||||||
False,
|
|
||||||
)
|
|
||||||
continue
|
|
||||||
|
|
||||||
if self.query.extra and col in self.query.extra:
|
|
||||||
if col in self.query.extra_select:
|
|
||||||
yield (
|
|
||||||
OrderBy(
|
|
||||||
Ref(col, RawSQL(*self.query.extra[col])),
|
|
||||||
descending=descending,
|
|
||||||
),
|
|
||||||
True,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
yield (
|
|
||||||
OrderBy(RawSQL(*self.query.extra[col]), descending=descending),
|
|
||||||
False,
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
if self.query.combinator and self.select:
|
if self.query.combinator and self.select:
|
||||||
# Don't use the first model's field because other
|
# Don't use the first model's field because other
|
||||||
@ -550,13 +510,8 @@ class SQLCompiler:
|
|||||||
"""
|
"""
|
||||||
if name in self.quote_cache:
|
if name in self.quote_cache:
|
||||||
return self.quote_cache[name]
|
return self.quote_cache[name]
|
||||||
if (
|
if (name in self.query.alias_map and name not in self.query.table_map) or (
|
||||||
(name in self.query.alias_map and name not in self.query.table_map)
|
self.query.external_aliases.get(name) and name not in self.query.table_map
|
||||||
or name in self.query.extra_select
|
|
||||||
or (
|
|
||||||
self.query.external_aliases.get(name)
|
|
||||||
and name not in self.query.table_map
|
|
||||||
)
|
|
||||||
):
|
):
|
||||||
self.quote_cache[name] = name
|
self.quote_cache[name] = name
|
||||||
return name
|
return name
|
||||||
@ -2044,7 +1999,6 @@ class SQLUpdateCompiler(SQLCompiler):
|
|||||||
query = self.query.chain(klass=Query)
|
query = self.query.chain(klass=Query)
|
||||||
query.select_related = False
|
query.select_related = False
|
||||||
query.clear_ordering(force=True)
|
query.clear_ordering(force=True)
|
||||||
query.extra = {}
|
|
||||||
query.select = []
|
query.select = []
|
||||||
meta = query.get_meta()
|
meta = query.get_meta()
|
||||||
fields = [meta.pk.name]
|
fields = [meta.pk.name]
|
||||||
|
@ -27,7 +27,6 @@ from django.db.models.expressions import (
|
|||||||
Exists,
|
Exists,
|
||||||
F,
|
F,
|
||||||
OuterRef,
|
OuterRef,
|
||||||
RawSQL,
|
|
||||||
Ref,
|
Ref,
|
||||||
ResolvedOuterRef,
|
ResolvedOuterRef,
|
||||||
Value,
|
Value,
|
||||||
@ -41,7 +40,7 @@ from django.db.models.query_utils import (
|
|||||||
)
|
)
|
||||||
from django.db.models.sql.constants import INNER, LOUTER, ORDER_DIR, SINGLE
|
from django.db.models.sql.constants import INNER, LOUTER, ORDER_DIR, SINGLE
|
||||||
from django.db.models.sql.datastructures import BaseTable, Empty, Join, MultiJoin
|
from django.db.models.sql.datastructures import BaseTable, Empty, Join, MultiJoin
|
||||||
from django.db.models.sql.where import AND, OR, ExtraWhere, NothingNode, WhereNode
|
from django.db.models.sql.where import AND, OR, NothingNode, WhereNode
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
from django.utils.regex_helper import _lazy_re_compile
|
from django.utils.regex_helper import _lazy_re_compile
|
||||||
from django.utils.tree import Node
|
from django.utils.tree import Node
|
||||||
@ -153,7 +152,6 @@ class RawQuery:
|
|||||||
# Mirror some properties of a normal query so that
|
# Mirror some properties of a normal query so that
|
||||||
# the compiler can be used to process results.
|
# the compiler can be used to process results.
|
||||||
self.low_mark, self.high_mark = 0, None # Used for offset/limit
|
self.low_mark, self.high_mark = 0, None # Used for offset/limit
|
||||||
self.extra_select = {}
|
|
||||||
self.annotation_select = {}
|
self.annotation_select = {}
|
||||||
|
|
||||||
def chain(self, using):
|
def chain(self, using):
|
||||||
@ -263,8 +261,9 @@ class Query(BaseExpression):
|
|||||||
# Arbitrary limit for select_related to prevents infinite recursion.
|
# Arbitrary limit for select_related to prevents infinite recursion.
|
||||||
max_depth = 5
|
max_depth = 5
|
||||||
# Holds the selects defined by a call to values() or values_list()
|
# Holds the selects defined by a call to values() or values_list()
|
||||||
# excluding annotation_select and extra_select.
|
# excluding annotation_select.
|
||||||
values_select = ()
|
values_select = ()
|
||||||
|
values_select_all = None
|
||||||
selected = None
|
selected = None
|
||||||
|
|
||||||
# SQL annotation-related attributes.
|
# SQL annotation-related attributes.
|
||||||
@ -276,13 +275,9 @@ class Query(BaseExpression):
|
|||||||
combinator_all = False
|
combinator_all = False
|
||||||
combined_queries = ()
|
combined_queries = ()
|
||||||
|
|
||||||
# These are for extensions. The contents are more or less appended verbatim
|
# This is for extensions. The contents are more or less appended verbatim
|
||||||
# to the appropriate clause.
|
# to the appropriate clause.
|
||||||
extra_select_mask = None
|
|
||||||
_extra_select_cache = None
|
|
||||||
|
|
||||||
extra_tables = ()
|
extra_tables = ()
|
||||||
extra_order_by = ()
|
|
||||||
|
|
||||||
# A tuple that is a set of model field names and either True, if these are
|
# A tuple that is a set of model field names and either True, if these are
|
||||||
# the fields to defer, or False if these are the only fields to load.
|
# the fields to defer, or False if these are the only fields to load.
|
||||||
@ -312,9 +307,6 @@ class Query(BaseExpression):
|
|||||||
self.where = WhereNode()
|
self.where = WhereNode()
|
||||||
# Maps alias -> Annotation Expression.
|
# Maps alias -> Annotation Expression.
|
||||||
self.annotations = {}
|
self.annotations = {}
|
||||||
# These are for extensions. The contents are more or less appended
|
|
||||||
# verbatim to the appropriate clause.
|
|
||||||
self.extra = {} # Maps col_alias -> (col_sql, params).
|
|
||||||
|
|
||||||
self._filtered_relations = {}
|
self._filtered_relations = {}
|
||||||
|
|
||||||
@ -401,11 +393,6 @@ class Query(BaseExpression):
|
|||||||
# It will get re-populated in the cloned queryset the next time it's
|
# It will get re-populated in the cloned queryset the next time it's
|
||||||
# used.
|
# used.
|
||||||
obj._annotation_select_cache = None
|
obj._annotation_select_cache = None
|
||||||
obj.extra = self.extra.copy()
|
|
||||||
if self.extra_select_mask is not None:
|
|
||||||
obj.extra_select_mask = self.extra_select_mask.copy()
|
|
||||||
if self._extra_select_cache is not None:
|
|
||||||
obj._extra_select_cache = self._extra_select_cache.copy()
|
|
||||||
if self.select_related is not False:
|
if self.select_related is not False:
|
||||||
# Use deepcopy because select_related stores fields in nested
|
# Use deepcopy because select_related stores fields in nested
|
||||||
# dicts.
|
# dicts.
|
||||||
@ -596,7 +583,6 @@ class Query(BaseExpression):
|
|||||||
self.select = ()
|
self.select = ()
|
||||||
self.selected = None
|
self.selected = None
|
||||||
self.default_cols = False
|
self.default_cols = False
|
||||||
self.extra = {}
|
|
||||||
if self.annotations:
|
if self.annotations:
|
||||||
# Inline reference to existing annotations and mask them as
|
# Inline reference to existing annotations and mask them as
|
||||||
# they are unnecessary given only the summarized aggregations
|
# they are unnecessary given only the summarized aggregations
|
||||||
@ -767,35 +753,17 @@ class Query(BaseExpression):
|
|||||||
w.relabel_aliases(change_map)
|
w.relabel_aliases(change_map)
|
||||||
self.where.add(w, connector)
|
self.where.add(w, connector)
|
||||||
|
|
||||||
# Selection columns and extra extensions are those provided by 'rhs'.
|
# Selection columns are those provided by 'rhs'.
|
||||||
if rhs.select:
|
if rhs.select:
|
||||||
self.set_select([col.relabeled_clone(change_map) for col in rhs.select])
|
self.set_select([col.relabeled_clone(change_map) for col in rhs.select])
|
||||||
else:
|
else:
|
||||||
self.select = ()
|
self.select = ()
|
||||||
|
|
||||||
if connector == OR:
|
|
||||||
# It would be nice to be able to handle this, but the queries don't
|
|
||||||
# really make sense (or return consistent value sets). Not worth
|
|
||||||
# the extra complexity when you can write a real query instead.
|
|
||||||
if self.extra and rhs.extra:
|
|
||||||
raise ValueError(
|
|
||||||
"When merging querysets using 'or', you cannot have "
|
|
||||||
"extra(select=...) on both sides."
|
|
||||||
)
|
|
||||||
self.extra.update(rhs.extra)
|
|
||||||
extra_select_mask = set()
|
|
||||||
if self.extra_select_mask is not None:
|
|
||||||
extra_select_mask.update(self.extra_select_mask)
|
|
||||||
if rhs.extra_select_mask is not None:
|
|
||||||
extra_select_mask.update(rhs.extra_select_mask)
|
|
||||||
if extra_select_mask:
|
|
||||||
self.set_extra_mask(extra_select_mask)
|
|
||||||
self.extra_tables += rhs.extra_tables
|
self.extra_tables += rhs.extra_tables
|
||||||
|
|
||||||
# Ordering uses the 'rhs' ordering, unless it has none, in which case
|
# Ordering uses the 'rhs' ordering, unless it has none, in which case
|
||||||
# the current ordering is used.
|
# the current ordering is used.
|
||||||
self.order_by = rhs.order_by or self.order_by
|
self.order_by = rhs.order_by or self.order_by
|
||||||
self.extra_order_by = rhs.extra_order_by or self.extra_order_by
|
|
||||||
|
|
||||||
def _get_defer_select_mask(self, opts, mask, select_mask=None):
|
def _get_defer_select_mask(self, opts, mask, select_mask=None):
|
||||||
if select_mask is None:
|
if select_mask is None:
|
||||||
@ -2162,19 +2130,19 @@ class Query(BaseExpression):
|
|||||||
self.select = ()
|
self.select = ()
|
||||||
self.default_cols = False
|
self.default_cols = False
|
||||||
self.select_related = False
|
self.select_related = False
|
||||||
self.set_extra_mask(())
|
|
||||||
self.set_annotation_mask(())
|
self.set_annotation_mask(())
|
||||||
self.selected = None
|
self.selected = None
|
||||||
|
|
||||||
def clear_select_fields(self):
|
def clear_select_fields(self):
|
||||||
"""
|
"""
|
||||||
Clear the list of fields to select (but not extra_select columns).
|
Clear the list of fields to select.
|
||||||
Some queryset types completely replace any existing list of select
|
Some queryset types completely replace any existing list of select
|
||||||
columns.
|
columns.
|
||||||
"""
|
"""
|
||||||
self.select = ()
|
self.select = ()
|
||||||
self.values_select = ()
|
self.values_select = ()
|
||||||
self.selected = None
|
self.selected = None
|
||||||
|
self.values_select_all = None
|
||||||
|
|
||||||
def add_select_col(self, col, name):
|
def add_select_col(self, col, name):
|
||||||
self.select += (col,)
|
self.select += (col,)
|
||||||
@ -2228,7 +2196,6 @@ class Query(BaseExpression):
|
|||||||
names = sorted(
|
names = sorted(
|
||||||
[
|
[
|
||||||
*get_field_names_from_opts(opts),
|
*get_field_names_from_opts(opts),
|
||||||
*self.extra,
|
|
||||||
*self.annotation_select,
|
*self.annotation_select,
|
||||||
*self._filtered_relations,
|
*self._filtered_relations,
|
||||||
]
|
]
|
||||||
@ -2255,8 +2222,6 @@ class Query(BaseExpression):
|
|||||||
item = item.removeprefix("-")
|
item = item.removeprefix("-")
|
||||||
if item in self.annotations:
|
if item in self.annotations:
|
||||||
continue
|
continue
|
||||||
if self.extra and item in self.extra:
|
|
||||||
continue
|
|
||||||
# names_to_path() validates the lookup. A descriptive
|
# names_to_path() validates the lookup. A descriptive
|
||||||
# FieldError will be raise if it's not.
|
# FieldError will be raise if it's not.
|
||||||
self.names_to_path(item.split(LOOKUP_SEP), self.model._meta)
|
self.names_to_path(item.split(LOOKUP_SEP), self.model._meta)
|
||||||
@ -2286,7 +2251,6 @@ class Query(BaseExpression):
|
|||||||
):
|
):
|
||||||
return
|
return
|
||||||
self.order_by = ()
|
self.order_by = ()
|
||||||
self.extra_order_by = ()
|
|
||||||
if clear_default:
|
if clear_default:
|
||||||
self.default_ordering = False
|
self.default_ordering = False
|
||||||
|
|
||||||
@ -2339,38 +2303,8 @@ class Query(BaseExpression):
|
|||||||
d = d.setdefault(part, {})
|
d = d.setdefault(part, {})
|
||||||
self.select_related = field_dict
|
self.select_related = field_dict
|
||||||
|
|
||||||
def add_extra(self, select, select_params, where, params, tables, order_by):
|
def add_extra_tables(self, tables):
|
||||||
"""
|
|
||||||
Add data to the various extra_* attributes for user-created additions
|
|
||||||
to the query.
|
|
||||||
"""
|
|
||||||
if select:
|
|
||||||
# We need to pair any placeholder markers in the 'select'
|
|
||||||
# dictionary with their parameters in 'select_params' so that
|
|
||||||
# subsequent updates to the select dictionary also adjust the
|
|
||||||
# parameters appropriately.
|
|
||||||
select_pairs = {}
|
|
||||||
if select_params:
|
|
||||||
param_iter = iter(select_params)
|
|
||||||
else:
|
|
||||||
param_iter = iter([])
|
|
||||||
for name, entry in select.items():
|
|
||||||
self.check_alias(name)
|
|
||||||
entry = str(entry)
|
|
||||||
entry_params = []
|
|
||||||
pos = entry.find("%s")
|
|
||||||
while pos != -1:
|
|
||||||
if pos == 0 or entry[pos - 1] != "%":
|
|
||||||
entry_params.append(next(param_iter))
|
|
||||||
pos = entry.find("%s", pos + 2)
|
|
||||||
select_pairs[name] = (entry, entry_params)
|
|
||||||
self.extra.update(select_pairs)
|
|
||||||
if where or params:
|
|
||||||
self.where.add(ExtraWhere(where, params), AND)
|
|
||||||
if tables:
|
|
||||||
self.extra_tables += tuple(tables)
|
self.extra_tables += tuple(tables)
|
||||||
if order_by:
|
|
||||||
self.extra_order_by = order_by
|
|
||||||
|
|
||||||
def clear_deferred_loading(self):
|
def clear_deferred_loading(self):
|
||||||
"""Remove any fields from the deferred loading set."""
|
"""Remove any fields from the deferred loading set."""
|
||||||
@ -2448,17 +2382,6 @@ class Query(BaseExpression):
|
|||||||
if self.annotation_select_mask is not None:
|
if self.annotation_select_mask is not None:
|
||||||
self.set_annotation_mask(self.annotation_select_mask.union(names))
|
self.set_annotation_mask(self.annotation_select_mask.union(names))
|
||||||
|
|
||||||
def set_extra_mask(self, names):
|
|
||||||
"""
|
|
||||||
Set the mask of extra select items that will be returned by SELECT.
|
|
||||||
Don't remove them from the Query since they might be used later.
|
|
||||||
"""
|
|
||||||
if names is None:
|
|
||||||
self.extra_select_mask = None
|
|
||||||
else:
|
|
||||||
self.extra_select_mask = set(names)
|
|
||||||
self._extra_select_cache = None
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def has_select_fields(self):
|
def has_select_fields(self):
|
||||||
return self.selected is not None
|
return self.selected is not None
|
||||||
@ -2473,20 +2396,16 @@ class Query(BaseExpression):
|
|||||||
for field in fields:
|
for field in fields:
|
||||||
self.check_alias(field)
|
self.check_alias(field)
|
||||||
field_names = []
|
field_names = []
|
||||||
extra_names = []
|
|
||||||
annotation_names = []
|
annotation_names = []
|
||||||
if not self.extra and not self.annotations:
|
if not self.annotations:
|
||||||
# Shortcut - if there are no extra or annotations, then
|
# Shortcut - if there are no annotations, then
|
||||||
# the values() clause must be just field names.
|
# the values() clause must be just field names.
|
||||||
field_names = list(fields)
|
field_names = list(fields)
|
||||||
selected = dict(zip(fields, range(len(fields))))
|
selected = dict(zip(fields, range(len(fields))))
|
||||||
else:
|
else:
|
||||||
self.default_cols = False
|
self.default_cols = False
|
||||||
for f in fields:
|
for f in fields:
|
||||||
if extra := self.extra_select.get(f):
|
if f in self.annotation_select:
|
||||||
extra_names.append(f)
|
|
||||||
selected[f] = RawSQL(*extra)
|
|
||||||
elif f in self.annotation_select:
|
|
||||||
annotation_names.append(f)
|
annotation_names.append(f)
|
||||||
selected[f] = f
|
selected[f] = f
|
||||||
elif f in self.annotations:
|
elif f in self.annotations:
|
||||||
@ -2502,7 +2421,6 @@ class Query(BaseExpression):
|
|||||||
self.names_to_path(f.split(LOOKUP_SEP), self.model._meta)
|
self.names_to_path(f.split(LOOKUP_SEP), self.model._meta)
|
||||||
selected[f] = len(field_names)
|
selected[f] = len(field_names)
|
||||||
field_names.append(f)
|
field_names.append(f)
|
||||||
self.set_extra_mask(extra_names)
|
|
||||||
self.set_annotation_mask(annotation_names)
|
self.set_annotation_mask(annotation_names)
|
||||||
else:
|
else:
|
||||||
field_names = [f.attname for f in self.model._meta.concrete_fields]
|
field_names = [f.attname for f in self.model._meta.concrete_fields]
|
||||||
@ -2528,6 +2446,7 @@ class Query(BaseExpression):
|
|||||||
self.group_by = tuple(group_by)
|
self.group_by = tuple(group_by)
|
||||||
|
|
||||||
self.values_select = tuple(field_names)
|
self.values_select = tuple(field_names)
|
||||||
|
self.values_select_all = not fields
|
||||||
self.add_fields(field_names, True)
|
self.add_fields(field_names, True)
|
||||||
self.selected = selected if fields else None
|
self.selected = selected if fields else None
|
||||||
|
|
||||||
@ -2551,20 +2470,6 @@ class Query(BaseExpression):
|
|||||||
else:
|
else:
|
||||||
return self.annotations
|
return self.annotations
|
||||||
|
|
||||||
@property
|
|
||||||
def extra_select(self):
|
|
||||||
if self._extra_select_cache is not None:
|
|
||||||
return self._extra_select_cache
|
|
||||||
if not self.extra:
|
|
||||||
return {}
|
|
||||||
elif self.extra_select_mask is not None:
|
|
||||||
self._extra_select_cache = {
|
|
||||||
k: v for k, v in self.extra.items() if k in self.extra_select_mask
|
|
||||||
}
|
|
||||||
return self._extra_select_cache
|
|
||||||
else:
|
|
||||||
return self.extra
|
|
||||||
|
|
||||||
def trim_start(self, names_with_path):
|
def trim_start(self, names_with_path):
|
||||||
"""
|
"""
|
||||||
Trim joins from the start of the join path. The candidates for trim
|
Trim joins from the start of the join path. The candidates for trim
|
||||||
|
@ -331,20 +331,6 @@ class NothingNode:
|
|||||||
raise EmptyResultSet
|
raise EmptyResultSet
|
||||||
|
|
||||||
|
|
||||||
class ExtraWhere:
|
|
||||||
# The contents are a black box - assume no aggregates or windows are used.
|
|
||||||
contains_aggregate = False
|
|
||||||
contains_over_clause = False
|
|
||||||
|
|
||||||
def __init__(self, sqls, params):
|
|
||||||
self.sqls = sqls
|
|
||||||
self.params = params
|
|
||||||
|
|
||||||
def as_sql(self, compiler=None, connection=None):
|
|
||||||
sqls = ["(%s)" % sql for sql in self.sqls]
|
|
||||||
return " AND ".join(sqls), list(self.params or ())
|
|
||||||
|
|
||||||
|
|
||||||
class SubqueryConstraint:
|
class SubqueryConstraint:
|
||||||
# Even if aggregates or windows would be used in a subquery,
|
# Even if aggregates or windows would be used in a subquery,
|
||||||
# the outer query isn't interested about those.
|
# the outer query isn't interested about those.
|
||||||
|
@ -44,10 +44,10 @@ from django.db.models.functions import (
|
|||||||
TruncDate,
|
TruncDate,
|
||||||
TruncHour,
|
TruncHour,
|
||||||
)
|
)
|
||||||
from django.test import TestCase
|
from django.test import TestCase, ignore_warnings, skipUnlessDBFeature
|
||||||
from django.test.testcases import skipUnlessDBFeature
|
|
||||||
from django.test.utils import Approximate, CaptureQueriesContext
|
from django.test.utils import Approximate, CaptureQueriesContext
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
from django.utils.deprecation import RemovedInDjango60Warning
|
||||||
|
|
||||||
from .models import Author, Book, Publisher, Store
|
from .models import Author, Book, Publisher, Store
|
||||||
|
|
||||||
@ -2139,6 +2139,8 @@ class AggregateTestCase(TestCase):
|
|||||||
with self.assertRaisesMessage(ValueError, msg):
|
with self.assertRaisesMessage(ValueError, msg):
|
||||||
Author.objects.aggregate(**{crafted_alias: Avg("age")})
|
Author.objects.aggregate(**{crafted_alias: Avg("age")})
|
||||||
|
|
||||||
|
# Entire test can be removed once deprecation period ends.
|
||||||
|
@ignore_warnings(category=RemovedInDjango60Warning)
|
||||||
def test_exists_extra_where_with_aggregate(self):
|
def test_exists_extra_where_with_aggregate(self):
|
||||||
qs = Book.objects.annotate(
|
qs = Book.objects.annotate(
|
||||||
count=Count("id"),
|
count=Count("id"),
|
||||||
|
@ -25,8 +25,9 @@ from django.db.models import (
|
|||||||
When,
|
When,
|
||||||
)
|
)
|
||||||
from django.db.models.functions import Cast, Concat
|
from django.db.models.functions import Cast, Concat
|
||||||
from django.test import TestCase, skipUnlessDBFeature
|
from django.test import TestCase, ignore_warnings, skipUnlessDBFeature
|
||||||
from django.test.utils import Approximate
|
from django.test.utils import Approximate
|
||||||
|
from django.utils.deprecation import RemovedInDjango60Warning
|
||||||
|
|
||||||
from .models import (
|
from .models import (
|
||||||
Alfa,
|
Alfa,
|
||||||
@ -238,6 +239,8 @@ class AggregationTests(TestCase):
|
|||||||
qs2 = books.filter(id__in=list(qs))
|
qs2 = books.filter(id__in=list(qs))
|
||||||
self.assertEqual(list(qs1), list(qs2))
|
self.assertEqual(list(qs1), list(qs2))
|
||||||
|
|
||||||
|
# Entire test can be removed once deprecation period ends.
|
||||||
|
@ignore_warnings(category=RemovedInDjango60Warning)
|
||||||
@skipUnlessDBFeature("supports_subqueries_in_group_by")
|
@skipUnlessDBFeature("supports_subqueries_in_group_by")
|
||||||
def test_annotate_with_extra(self):
|
def test_annotate_with_extra(self):
|
||||||
"""
|
"""
|
||||||
@ -287,14 +290,18 @@ class AggregationTests(TestCase):
|
|||||||
{"pages__sum": 3703, "pages__avg": Approximate(617.166, places=2)},
|
{"pages__sum": 3703, "pages__avg": Approximate(617.166, places=2)},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Entire block can be removed once deprecation period ends.
|
||||||
|
with ignore_warnings(category=RemovedInDjango60Warning):
|
||||||
# Aggregate overrides extra selected column
|
# Aggregate overrides extra selected column
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
Book.objects.extra(select={"price_per_page": "price / pages"}).aggregate(
|
Book.objects.extra(
|
||||||
Sum("pages")
|
select={"price_per_page": "price / pages"}
|
||||||
),
|
).aggregate(Sum("pages")),
|
||||||
{"pages__sum": 3703},
|
{"pages__sum": 3703},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Entire test can be removed once deprecation period ends.
|
||||||
|
@ignore_warnings(category=RemovedInDjango60Warning)
|
||||||
def test_annotation(self):
|
def test_annotation(self):
|
||||||
# Annotations get combined with extra select clauses
|
# Annotations get combined with extra select clauses
|
||||||
obj = (
|
obj = (
|
||||||
@ -879,6 +886,8 @@ class AggregationTests(TestCase):
|
|||||||
|
|
||||||
# Regression for #10132 - If the values() clause only mentioned extra
|
# Regression for #10132 - If the values() clause only mentioned extra
|
||||||
# (select=) columns, those columns are used for grouping
|
# (select=) columns, those columns are used for grouping
|
||||||
|
# Entire block can be removed once deprecation period ends.
|
||||||
|
with ignore_warnings(category=RemovedInDjango60Warning):
|
||||||
qs = (
|
qs = (
|
||||||
Book.objects.extra(select={"pub": "publisher_id"})
|
Book.objects.extra(select={"pub": "publisher_id"})
|
||||||
.values("pub")
|
.values("pub")
|
||||||
@ -1058,6 +1067,8 @@ class AggregationTests(TestCase):
|
|||||||
|
|
||||||
# Regression for #10290 - extra selects with parameters can be used for
|
# Regression for #10290 - extra selects with parameters can be used for
|
||||||
# grouping.
|
# grouping.
|
||||||
|
# Entire block can be removed once deprecation period ends.
|
||||||
|
with ignore_warnings(category=RemovedInDjango60Warning):
|
||||||
qs = (
|
qs = (
|
||||||
Book.objects.annotate(mean_auth_age=Avg("authors__age"))
|
Book.objects.annotate(mean_auth_age=Avg("authors__age"))
|
||||||
.extra(select={"sheets": "(pages + %s) / %s"}, select_params=[1, 2])
|
.extra(select={"sheets": "(pages + %s) / %s"}, select_params=[1, 2])
|
||||||
|
@ -37,8 +37,9 @@ from django.db.models.functions import (
|
|||||||
Trim,
|
Trim,
|
||||||
)
|
)
|
||||||
from django.db.models.sql.query import get_field_names_from_opts
|
from django.db.models.sql.query import get_field_names_from_opts
|
||||||
from django.test import TestCase, skipUnlessDBFeature
|
from django.test import TestCase, ignore_warnings, skipUnlessDBFeature
|
||||||
from django.test.utils import register_lookup
|
from django.test.utils import register_lookup
|
||||||
|
from django.utils.deprecation import RemovedInDjango60Warning
|
||||||
|
|
||||||
from .models import (
|
from .models import (
|
||||||
Author,
|
Author,
|
||||||
@ -722,10 +723,9 @@ class NonAggregateAnnotationTestCase(TestCase):
|
|||||||
Columns are aligned in the correct order for resolve_columns. This test
|
Columns are aligned in the correct order for resolve_columns. This test
|
||||||
will fail on MySQL if column ordering is out. Column fields should be
|
will fail on MySQL if column ordering is out. Column fields should be
|
||||||
aligned as:
|
aligned as:
|
||||||
1. extra_select
|
1. model_fields
|
||||||
2. model_fields
|
2. annotation_fields
|
||||||
3. annotation_fields
|
3. model_related_fields
|
||||||
4. model_related_fields
|
|
||||||
"""
|
"""
|
||||||
store = Store.objects.first()
|
store = Store.objects.first()
|
||||||
Employee.objects.create(
|
Employee.objects.create(
|
||||||
@ -747,6 +747,8 @@ class NonAggregateAnnotationTestCase(TestCase):
|
|||||||
salary=Decimal(40000.00),
|
salary=Decimal(40000.00),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# random_value annotation can be removed once deprecation period ends.
|
||||||
|
with ignore_warnings(category=RemovedInDjango60Warning):
|
||||||
qs = (
|
qs = (
|
||||||
Employee.objects.extra(select={"random_value": "42"})
|
Employee.objects.extra(select={"random_value": "42"})
|
||||||
.select_related("store")
|
.select_related("store")
|
||||||
@ -797,6 +799,8 @@ class NonAggregateAnnotationTestCase(TestCase):
|
|||||||
salary=Decimal(40000.00),
|
salary=Decimal(40000.00),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# random_value annotation can be removed once deprecation period ends.
|
||||||
|
with ignore_warnings(category=RemovedInDjango60Warning):
|
||||||
qs = (
|
qs = (
|
||||||
Employee.objects.extra(select={"random_value": "42"})
|
Employee.objects.extra(select={"random_value": "42"})
|
||||||
.select_related("store")
|
.select_related("store")
|
||||||
|
@ -455,6 +455,8 @@ class ModelTest(TestCase):
|
|||||||
s = {a10, a11, a12}
|
s = {a10, a11, a12}
|
||||||
self.assertIn(Article.objects.get(headline="Article 11"), s)
|
self.assertIn(Article.objects.get(headline="Article 11"), s)
|
||||||
|
|
||||||
|
# Entire test can be removed once deprecation period ends.
|
||||||
|
@ignore_warnings(category=RemovedInDjango60Warning)
|
||||||
def test_extra_method_select_argument_with_dashes_and_values(self):
|
def test_extra_method_select_argument_with_dashes_and_values(self):
|
||||||
# The 'select' argument to extra() supports names with dashes in
|
# The 'select' argument to extra() supports names with dashes in
|
||||||
# them, as long as you use values().
|
# them, as long as you use values().
|
||||||
@ -483,6 +485,8 @@ class ModelTest(TestCase):
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Entire test can be removed once deprecation period ends.
|
||||||
|
@ignore_warnings(category=RemovedInDjango60Warning)
|
||||||
def test_extra_method_select_argument_with_dashes(self):
|
def test_extra_method_select_argument_with_dashes(self):
|
||||||
# If you use 'select' with extra() and names containing dashes on a
|
# If you use 'select' with extra() and names containing dashes on a
|
||||||
# query that's *not* a values() query, those extra 'select' values
|
# query that's *not* a values() query, those extra 'select' values
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
from django.core.exceptions import FieldDoesNotExist, FieldError
|
from django.core.exceptions import FieldDoesNotExist, FieldError
|
||||||
from django.test import SimpleTestCase, TestCase
|
from django.test import SimpleTestCase, TestCase, ignore_warnings
|
||||||
|
from django.utils.deprecation import RemovedInDjango60Warning
|
||||||
|
|
||||||
from .models import (
|
from .models import (
|
||||||
BigChild,
|
BigChild,
|
||||||
@ -82,6 +83,8 @@ class DeferTests(AssertionMixin, TestCase):
|
|||||||
with self.assertRaisesMessage(TypeError, msg):
|
with self.assertRaisesMessage(TypeError, msg):
|
||||||
Primary.objects.only(None)
|
Primary.objects.only(None)
|
||||||
|
|
||||||
|
# Entire test can be removed once deprecation period ends.
|
||||||
|
@ignore_warnings(category=RemovedInDjango60Warning)
|
||||||
def test_defer_extra(self):
|
def test_defer_extra(self):
|
||||||
qs = Primary.objects.all()
|
qs = Primary.objects.all()
|
||||||
self.assert_delayed(qs.defer("name").extra(select={"a": 1})[0], 1)
|
self.assert_delayed(qs.defer("name").extra(select={"a": 1})[0], 1)
|
||||||
|
@ -6,8 +6,10 @@ from django.test import (
|
|||||||
SimpleTestCase,
|
SimpleTestCase,
|
||||||
TestCase,
|
TestCase,
|
||||||
TransactionTestCase,
|
TransactionTestCase,
|
||||||
|
ignore_warnings,
|
||||||
skipUnlessDBFeature,
|
skipUnlessDBFeature,
|
||||||
)
|
)
|
||||||
|
from django.utils.deprecation import RemovedInDjango60Warning
|
||||||
|
|
||||||
from .models import (
|
from .models import (
|
||||||
Award,
|
Award,
|
||||||
@ -309,6 +311,8 @@ class Ticket19102Tests(TestCase):
|
|||||||
self.assertFalse(Login.objects.filter(pk=self.l1.pk).exists())
|
self.assertFalse(Login.objects.filter(pk=self.l1.pk).exists())
|
||||||
self.assertTrue(Login.objects.filter(pk=self.l2.pk).exists())
|
self.assertTrue(Login.objects.filter(pk=self.l2.pk).exists())
|
||||||
|
|
||||||
|
# Entire block can be removed once deprecation period ends.
|
||||||
|
@ignore_warnings(category=RemovedInDjango60Warning)
|
||||||
@skipUnlessDBFeature("update_can_self_select")
|
@skipUnlessDBFeature("update_can_self_select")
|
||||||
def test_ticket_19102_extra(self):
|
def test_ticket_19102_extra(self):
|
||||||
with self.assertNumQueries(1):
|
with self.assertNumQueries(1):
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.test import TestCase
|
from django.test import SimpleTestCase, TestCase, ignore_warnings
|
||||||
|
from django.utils.deprecation import RemovedInDjango60Warning
|
||||||
|
|
||||||
from .models import Order, RevisionableModel, TestObject
|
from .models import Order, RevisionableModel, TestObject
|
||||||
|
|
||||||
|
|
||||||
|
@ignore_warnings(category=RemovedInDjango60Warning)
|
||||||
class ExtraRegressTests(TestCase):
|
class ExtraRegressTests(TestCase):
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpTestData(cls):
|
def setUpTestData(cls):
|
||||||
@ -284,7 +286,7 @@ class ExtraRegressTests(TestCase):
|
|||||||
select={"foo": "first", "bar": "second", "whiz": "third"}
|
select={"foo": "first", "bar": "second", "whiz": "third"}
|
||||||
).values_list()
|
).values_list()
|
||||||
),
|
),
|
||||||
[("first", "second", "third", obj.pk, "first", "second", "third")],
|
[(obj.pk, "first", "second", "third", "first", "second", "third")],
|
||||||
)
|
)
|
||||||
|
|
||||||
# Extra columns after an empty values_list() are still included
|
# Extra columns after an empty values_list() are still included
|
||||||
@ -294,7 +296,7 @@ class ExtraRegressTests(TestCase):
|
|||||||
select={"foo": "first", "bar": "second", "whiz": "third"}
|
select={"foo": "first", "bar": "second", "whiz": "third"}
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
[("first", "second", "third", obj.pk, "first", "second", "third")],
|
[(obj.pk, "first", "second", "third", "first", "second", "third")],
|
||||||
)
|
)
|
||||||
|
|
||||||
# Extra columns ignored completely if not mentioned in values_list()
|
# Extra columns ignored completely if not mentioned in values_list()
|
||||||
@ -468,3 +470,23 @@ class ExtraRegressTests(TestCase):
|
|||||||
self.assertSequenceEqual(
|
self.assertSequenceEqual(
|
||||||
qs.order_by("-second_extra").values_list("first"), [("a",), ("a",)]
|
qs.order_by("-second_extra").values_list("first"), [("a",), ("a",)]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ExtraDeprecationTests(SimpleTestCase):
|
||||||
|
def test_extra_select_deprecation(self):
|
||||||
|
msg = "extra(select) usage is deprecated, use annotate() with RawSQL instead."
|
||||||
|
with self.assertWarnsMessage(RemovedInDjango60Warning, msg) as ctx:
|
||||||
|
TestObject.objects.all().extra(select={"select": "%s"}, select_params=(1,))
|
||||||
|
self.assertEqual(ctx.filename, __file__)
|
||||||
|
|
||||||
|
def test_extra_where_deprecation(self):
|
||||||
|
msg = "extra(where) usage is deprecated, use filter() with RawSQL instead."
|
||||||
|
with self.assertWarnsMessage(RemovedInDjango60Warning, msg) as ctx:
|
||||||
|
TestObject.objects.all().extra(where={"where": "foo = %s"}, params=(1,))
|
||||||
|
self.assertEqual(ctx.filename, __file__)
|
||||||
|
|
||||||
|
def test_extra_order_by_deprecation(self):
|
||||||
|
msg = "extra(order_by) usage is deprecated, use order_by() instead."
|
||||||
|
with self.assertWarnsMessage(RemovedInDjango60Warning, msg) as ctx:
|
||||||
|
TestObject.objects.all().extra(order_by={"order_by": "random()"})
|
||||||
|
self.assertEqual(ctx.filename, __file__)
|
||||||
|
@ -18,8 +18,9 @@ from django.db.models import (
|
|||||||
)
|
)
|
||||||
from django.db.models.functions import Concat
|
from django.db.models.functions import Concat
|
||||||
from django.db.models.lookups import Exact, IStartsWith
|
from django.db.models.lookups import Exact, IStartsWith
|
||||||
from django.test import TestCase
|
from django.test import TestCase, ignore_warnings
|
||||||
from django.test.testcases import skipUnlessDBFeature
|
from django.test.testcases import skipUnlessDBFeature
|
||||||
|
from django.utils.deprecation import RemovedInDjango60Warning
|
||||||
|
|
||||||
from .models import (
|
from .models import (
|
||||||
Author,
|
Author,
|
||||||
@ -344,6 +345,8 @@ class FilteredRelationTests(TestCase):
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Entire test can be removed once deprecation period ends.
|
||||||
|
@ignore_warnings(category=RemovedInDjango60Warning)
|
||||||
def test_extra(self):
|
def test_extra(self):
|
||||||
self.assertSequenceEqual(
|
self.assertSequenceEqual(
|
||||||
Author.objects.annotate(
|
Author.objects.annotate(
|
||||||
|
@ -28,8 +28,9 @@ from django.db.models.lookups import (
|
|||||||
LessThan,
|
LessThan,
|
||||||
LessThanOrEqual,
|
LessThanOrEqual,
|
||||||
)
|
)
|
||||||
from django.test import TestCase, skipUnlessDBFeature
|
from django.test import TestCase, ignore_warnings, skipUnlessDBFeature
|
||||||
from django.test.utils import isolate_apps, register_lookup
|
from django.test.utils import isolate_apps, register_lookup
|
||||||
|
from django.utils.deprecation import RemovedInDjango60Warning
|
||||||
|
|
||||||
from .models import (
|
from .models import (
|
||||||
Article,
|
Article,
|
||||||
@ -379,6 +380,8 @@ class LookupTests(TestCase):
|
|||||||
{"headline": "Article 1", "id": self.a1.id},
|
{"headline": "Article 1", "id": self.a1.id},
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
# Entire block can be removed once deprecation period ends.
|
||||||
|
with ignore_warnings(category=RemovedInDjango60Warning):
|
||||||
# The values() method works with "extra" fields specified in extra(select).
|
# The values() method works with "extra" fields specified in extra(select).
|
||||||
self.assertSequenceEqual(
|
self.assertSequenceEqual(
|
||||||
Article.objects.extra(select={"id_plus_one": "id + 1"}).values(
|
Article.objects.extra(select={"id_plus_one": "id + 1"}).values(
|
||||||
@ -500,6 +503,8 @@ class LookupTests(TestCase):
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
# Entire block can be removed once deprecation period ends.
|
||||||
|
with ignore_warnings(category=RemovedInDjango60Warning):
|
||||||
# However, an exception FieldDoesNotExist will be thrown if you specify
|
# However, an exception FieldDoesNotExist will be thrown if you specify
|
||||||
# a nonexistent field name in values() (a field that is neither in the
|
# a nonexistent field name in values() (a field that is neither in the
|
||||||
# model nor in extra(select)).
|
# model nor in extra(select)).
|
||||||
@ -566,6 +571,8 @@ class LookupTests(TestCase):
|
|||||||
self.a7.id,
|
self.a7.id,
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
# Entire block can be removed once deprecation period ends.
|
||||||
|
with ignore_warnings(category=RemovedInDjango60Warning):
|
||||||
self.assertSequenceEqual(
|
self.assertSequenceEqual(
|
||||||
Article.objects.extra(select={"id_plus_one": "id+1"})
|
Article.objects.extra(select={"id_plus_one": "id+1"})
|
||||||
.order_by("id")
|
.order_by("id")
|
||||||
|
@ -3,7 +3,7 @@ from copy import deepcopy
|
|||||||
|
|
||||||
from django.core.exceptions import FieldError, MultipleObjectsReturned
|
from django.core.exceptions import FieldError, MultipleObjectsReturned
|
||||||
from django.db import IntegrityError, models, transaction
|
from django.db import IntegrityError, models, transaction
|
||||||
from django.test import TestCase
|
from django.test import TestCase, ignore_warnings
|
||||||
from django.utils.deprecation import RemovedInDjango60Warning
|
from django.utils.deprecation import RemovedInDjango60Warning
|
||||||
from django.utils.translation import gettext_lazy
|
from django.utils.translation import gettext_lazy
|
||||||
|
|
||||||
@ -282,6 +282,8 @@ class ManyToOneTests(TestCase):
|
|||||||
queryset.query.get_compiler(queryset.db).as_sql()[0].count("INNER JOIN"), 1
|
queryset.query.get_compiler(queryset.db).as_sql()[0].count("INNER JOIN"), 1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Entire block can be removed once deprecation period ends.
|
||||||
|
with ignore_warnings(category=RemovedInDjango60Warning):
|
||||||
# The automatically joined table has a predictable name.
|
# The automatically joined table has a predictable name.
|
||||||
self.assertSequenceEqual(
|
self.assertSequenceEqual(
|
||||||
Article.objects.filter(reporter__first_name__exact="John").extra(
|
Article.objects.filter(reporter__first_name__exact="John").extra(
|
||||||
@ -570,10 +572,14 @@ class ManyToOneTests(TestCase):
|
|||||||
reporter_fields = ", ".join(sorted(f.name for f in Reporter._meta.get_fields()))
|
reporter_fields = ", ".join(sorted(f.name for f in Reporter._meta.get_fields()))
|
||||||
with self.assertRaisesMessage(FieldError, expected_message % reporter_fields):
|
with self.assertRaisesMessage(FieldError, expected_message % reporter_fields):
|
||||||
Article.objects.values_list("reporter__notafield")
|
Article.objects.values_list("reporter__notafield")
|
||||||
|
# Entire block can be removed once deprecation period ends.
|
||||||
|
with ignore_warnings(category=RemovedInDjango60Warning):
|
||||||
article_fields = ", ".join(
|
article_fields = ", ".join(
|
||||||
["EXTRA"] + sorted(f.name for f in Article._meta.get_fields())
|
["EXTRA"] + sorted(f.name for f in Article._meta.get_fields())
|
||||||
)
|
)
|
||||||
with self.assertRaisesMessage(FieldError, expected_message % article_fields):
|
with self.assertRaisesMessage(
|
||||||
|
FieldError, expected_message % article_fields
|
||||||
|
):
|
||||||
Article.objects.extra(select={"EXTRA": "EXTRA_SELECT"}).values_list(
|
Article.objects.extra(select={"EXTRA": "EXTRA_SELECT"}).values_list(
|
||||||
"notafield"
|
"notafield"
|
||||||
)
|
)
|
||||||
|
@ -14,7 +14,8 @@ from django.db.models import (
|
|||||||
Value,
|
Value,
|
||||||
)
|
)
|
||||||
from django.db.models.functions import Length, Upper
|
from django.db.models.functions import Length, Upper
|
||||||
from django.test import TestCase
|
from django.test import TestCase, ignore_warnings
|
||||||
|
from django.utils.deprecation import RemovedInDjango60Warning
|
||||||
|
|
||||||
from .models import (
|
from .models import (
|
||||||
Article,
|
Article,
|
||||||
@ -335,6 +336,8 @@ class OrderingTests(TestCase):
|
|||||||
with self.assertRaisesMessage(TypeError, msg):
|
with self.assertRaisesMessage(TypeError, msg):
|
||||||
qs.last()
|
qs.last()
|
||||||
|
|
||||||
|
# Entire test can be removed once deprecation period ends.
|
||||||
|
@ignore_warnings(category=RemovedInDjango60Warning)
|
||||||
def test_extra_ordering(self):
|
def test_extra_ordering(self):
|
||||||
"""
|
"""
|
||||||
Ordering can be based on fields included from an 'extra' clause
|
Ordering can be based on fields included from an 'extra' clause
|
||||||
@ -352,6 +355,8 @@ class OrderingTests(TestCase):
|
|||||||
attrgetter("headline"),
|
attrgetter("headline"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Entire test can be removed once deprecation period ends.
|
||||||
|
@ignore_warnings(category=RemovedInDjango60Warning)
|
||||||
def test_extra_ordering_quoting(self):
|
def test_extra_ordering_quoting(self):
|
||||||
"""
|
"""
|
||||||
If the extra clause uses an SQL keyword for a name, it will be
|
If the extra clause uses an SQL keyword for a name, it will be
|
||||||
@ -370,6 +375,8 @@ class OrderingTests(TestCase):
|
|||||||
attrgetter("headline"),
|
attrgetter("headline"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Entire test can be removed once deprecation period ends.
|
||||||
|
@ignore_warnings(category=RemovedInDjango60Warning)
|
||||||
def test_extra_ordering_with_table_name(self):
|
def test_extra_ordering_with_table_name(self):
|
||||||
self.assertQuerySetEqual(
|
self.assertQuerySetEqual(
|
||||||
Article.objects.extra(order_by=["ordering_article.headline"]),
|
Article.objects.extra(order_by=["ordering_article.headline"]),
|
||||||
|
@ -12,7 +12,8 @@ from django.db.models import (
|
|||||||
)
|
)
|
||||||
from django.db.models.functions import Mod
|
from django.db.models.functions import Mod
|
||||||
from django.test import TestCase, skipIfDBFeature, skipUnlessDBFeature
|
from django.test import TestCase, skipIfDBFeature, skipUnlessDBFeature
|
||||||
from django.test.utils import CaptureQueriesContext
|
from django.test.utils import CaptureQueriesContext, ignore_warnings
|
||||||
|
from django.utils.deprecation import RemovedInDjango60Warning
|
||||||
|
|
||||||
from .models import Author, Celebrity, ExtraInfo, Number, ReservedName
|
from .models import Author, Celebrity, ExtraInfo, Number, ReservedName
|
||||||
|
|
||||||
@ -280,6 +281,8 @@ class QuerySetSetOperationTests(TestCase):
|
|||||||
)
|
)
|
||||||
self.assertCountEqual(qs1.union(qs2), [(1, 0), (0, 2)])
|
self.assertCountEqual(qs1.union(qs2), [(1, 0), (0, 2)])
|
||||||
|
|
||||||
|
# Entire test can be removed once deprecation period ends.
|
||||||
|
@ignore_warnings(category=RemovedInDjango60Warning)
|
||||||
def test_union_with_extra_and_values_list(self):
|
def test_union_with_extra_and_values_list(self):
|
||||||
qs1 = (
|
qs1 = (
|
||||||
Number.objects.filter(num=1)
|
Number.objects.filter(num=1)
|
||||||
@ -408,6 +411,8 @@ class QuerySetSetOperationTests(TestCase):
|
|||||||
[reserved_name.pk],
|
[reserved_name.pk],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Entire test can be removed once deprecation period ends.
|
||||||
|
@ignore_warnings(category=RemovedInDjango60Warning)
|
||||||
def test_union_multiple_models_with_values_list_and_order_by_extra_select(self):
|
def test_union_multiple_models_with_values_list_and_order_by_extra_select(self):
|
||||||
reserved_name = ReservedName.objects.create(name="rn1", order=0)
|
reserved_name = ReservedName.objects.create(name="rn1", order=0)
|
||||||
qs1 = Celebrity.objects.extra(select={"extra_name": "name"})
|
qs1 = Celebrity.objects.extra(select={"extra_name": "name"})
|
||||||
|
@ -12,7 +12,8 @@ from django.db.models.functions import ExtractYear, Length, LTrim
|
|||||||
from django.db.models.sql.constants import LOUTER
|
from django.db.models.sql.constants import LOUTER
|
||||||
from django.db.models.sql.where import AND, OR, NothingNode, WhereNode
|
from django.db.models.sql.where import AND, OR, NothingNode, WhereNode
|
||||||
from django.test import SimpleTestCase, TestCase, skipUnlessDBFeature
|
from django.test import SimpleTestCase, TestCase, skipUnlessDBFeature
|
||||||
from django.test.utils import CaptureQueriesContext, register_lookup
|
from django.test.utils import CaptureQueriesContext, ignore_warnings, register_lookup
|
||||||
|
from django.utils.deprecation import RemovedInDjango60Warning
|
||||||
|
|
||||||
from .models import (
|
from .models import (
|
||||||
FK1,
|
FK1,
|
||||||
@ -325,6 +326,8 @@ class Queries1Tests(TestCase):
|
|||||||
.count(),
|
.count(),
|
||||||
4,
|
4,
|
||||||
)
|
)
|
||||||
|
# Entire block can be removed once deprecation period ends.
|
||||||
|
with ignore_warnings(category=RemovedInDjango60Warning):
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
(
|
(
|
||||||
Item.objects.exclude(name="two")
|
Item.objects.exclude(name="two")
|
||||||
@ -594,6 +597,8 @@ class Queries1Tests(TestCase):
|
|||||||
with self.assertRaisesMessage(TypeError, msg):
|
with self.assertRaisesMessage(TypeError, msg):
|
||||||
Author.objects.all() | Tag.objects.all()
|
Author.objects.all() | Tag.objects.all()
|
||||||
|
|
||||||
|
# Entire test can be removed once deprecation period ends.
|
||||||
|
@ignore_warnings(category=RemovedInDjango60Warning)
|
||||||
def test_ticket3141(self):
|
def test_ticket3141(self):
|
||||||
self.assertEqual(Author.objects.extra(select={"foo": "1"}).count(), 4)
|
self.assertEqual(Author.objects.extra(select={"foo": "1"}).count(), 4)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
@ -750,6 +755,8 @@ class Queries1Tests(TestCase):
|
|||||||
datetime.datetime(2007, 12, 19, 0, 0),
|
datetime.datetime(2007, 12, 19, 0, 0),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Entire test can be removed once deprecation period ends.
|
||||||
|
@ignore_warnings(category=RemovedInDjango60Warning)
|
||||||
def test_tickets_7087_12242(self):
|
def test_tickets_7087_12242(self):
|
||||||
# Dates with extra select columns
|
# Dates with extra select columns
|
||||||
self.assertSequenceEqual(
|
self.assertSequenceEqual(
|
||||||
@ -893,9 +900,13 @@ class Queries1Tests(TestCase):
|
|||||||
self.assertSequenceEqual(q.annotate(Count("food")), [])
|
self.assertSequenceEqual(q.annotate(Count("food")), [])
|
||||||
self.assertSequenceEqual(q.order_by("meal", "food"), [])
|
self.assertSequenceEqual(q.order_by("meal", "food"), [])
|
||||||
self.assertSequenceEqual(q.distinct(), [])
|
self.assertSequenceEqual(q.distinct(), [])
|
||||||
|
# Block can be removed once deprecation period ends.
|
||||||
|
with ignore_warnings(category=RemovedInDjango60Warning):
|
||||||
self.assertSequenceEqual(q.extra(select={"foo": "1"}), [])
|
self.assertSequenceEqual(q.extra(select={"foo": "1"}), [])
|
||||||
self.assertSequenceEqual(q.reverse(), [])
|
self.assertSequenceEqual(q.reverse(), [])
|
||||||
q.query.low_mark = 1
|
q.query.low_mark = 1
|
||||||
|
# Block can be removed once deprecation period ends.
|
||||||
|
with ignore_warnings(category=RemovedInDjango60Warning):
|
||||||
msg = "Cannot change a query once a slice has been taken."
|
msg = "Cannot change a query once a slice has been taken."
|
||||||
with self.assertRaisesMessage(TypeError, msg):
|
with self.assertRaisesMessage(TypeError, msg):
|
||||||
q.extra(select={"foo": "1"})
|
q.extra(select={"foo": "1"})
|
||||||
@ -1866,8 +1877,11 @@ class Queries5Tests(TestCase):
|
|||||||
[self.rank1, self.rank2, self.rank3],
|
[self.rank1, self.rank2, self.rank3],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Block should be changed to use extra(tables).order_by() when
|
||||||
|
# deprecation period ends.
|
||||||
# Ordering of extra() pieces is possible, too and you can mix extra
|
# Ordering of extra() pieces is possible, too and you can mix extra
|
||||||
# fields and model fields in the ordering.
|
# fields and model fields in the ordering.
|
||||||
|
with ignore_warnings(category=RemovedInDjango60Warning):
|
||||||
self.assertSequenceEqual(
|
self.assertSequenceEqual(
|
||||||
Ranking.objects.extra(
|
Ranking.objects.extra(
|
||||||
tables=["django_site"], order_by=["-django_site.id", "rank"]
|
tables=["django_site"], order_by=["-django_site.id", "rank"]
|
||||||
@ -1875,7 +1889,11 @@ class Queries5Tests(TestCase):
|
|||||||
[self.rank1, self.rank2, self.rank3],
|
[self.rank1, self.rank2, self.rank3],
|
||||||
)
|
)
|
||||||
|
|
||||||
sql = "case when %s > 2 then 1 else 0 end" % connection.ops.quote_name("rank")
|
# Entire block can be removed once deprecation period ends.
|
||||||
|
with ignore_warnings(category=RemovedInDjango60Warning):
|
||||||
|
sql = "case when %s > 2 then 1 else 0 end" % connection.ops.quote_name(
|
||||||
|
"rank"
|
||||||
|
)
|
||||||
qs = Ranking.objects.extra(select={"good": sql})
|
qs = Ranking.objects.extra(select={"good": sql})
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
[o.good for o in qs.extra(order_by=("-good",))], [True, False, False]
|
[o.good for o in qs.extra(order_by=("-good",))], [True, False, False]
|
||||||
@ -1890,6 +1908,8 @@ class Queries5Tests(TestCase):
|
|||||||
dicts = qs.values("id", "rank").order_by("id")
|
dicts = qs.values("id", "rank").order_by("id")
|
||||||
self.assertEqual([d["rank"] for d in dicts], [2, 1, 3])
|
self.assertEqual([d["rank"] for d in dicts], [2, 1, 3])
|
||||||
|
|
||||||
|
# Entire test can be removed once deprecation period ends.
|
||||||
|
@ignore_warnings(category=RemovedInDjango60Warning)
|
||||||
def test_ticket7256(self):
|
def test_ticket7256(self):
|
||||||
# An empty values() call includes all aliases, including those from an
|
# An empty values() call includes all aliases, including those from an
|
||||||
# extra()
|
# extra()
|
||||||
@ -1962,6 +1982,8 @@ class Queries5Tests(TestCase):
|
|||||||
[self.n1, self.n2],
|
[self.n1, self.n2],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Entire test can be removed once deprecation period ends.
|
||||||
|
@ignore_warnings(category=RemovedInDjango60Warning)
|
||||||
def test_extra_select_literal_percent_s(self):
|
def test_extra_select_literal_percent_s(self):
|
||||||
# Allow %%s to escape select clauses
|
# Allow %%s to escape select clauses
|
||||||
self.assertEqual(Note.objects.extra(select={"foo": "'%%s'"})[0].foo, "%s")
|
self.assertEqual(Note.objects.extra(select={"foo": "'%%s'"})[0].foo, "%s")
|
||||||
@ -1972,6 +1994,8 @@ class Queries5Tests(TestCase):
|
|||||||
Note.objects.extra(select={"foo": "'bar %%s'"})[0].foo, "bar %s"
|
Note.objects.extra(select={"foo": "'bar %%s'"})[0].foo, "bar %s"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Entire test can be removed once deprecation period ends.
|
||||||
|
@ignore_warnings(category=RemovedInDjango60Warning)
|
||||||
def test_extra_select_alias_sql_injection(self):
|
def test_extra_select_alias_sql_injection(self):
|
||||||
crafted_alias = """injected_name" from "queries_note"; --"""
|
crafted_alias = """injected_name" from "queries_note"; --"""
|
||||||
msg = (
|
msg = (
|
||||||
@ -2350,6 +2374,8 @@ class QuerysetOrderedTests(unittest.TestCase):
|
|||||||
def test_empty_queryset(self):
|
def test_empty_queryset(self):
|
||||||
self.assertIs(Annotation.objects.none().ordered, True)
|
self.assertIs(Annotation.objects.none().ordered, True)
|
||||||
|
|
||||||
|
# Entire test can be removed once deprecation period ends.
|
||||||
|
@ignore_warnings(category=RemovedInDjango60Warning)
|
||||||
def test_order_by_extra(self):
|
def test_order_by_extra(self):
|
||||||
self.assertIs(Annotation.objects.extra(order_by=["id"]).ordered, True)
|
self.assertIs(Annotation.objects.extra(order_by=["id"]).ordered, True)
|
||||||
|
|
||||||
@ -2682,6 +2708,8 @@ class ValuesQuerysetTests(TestCase):
|
|||||||
qs = qs.values_list("num", flat=True)
|
qs = qs.values_list("num", flat=True)
|
||||||
self.assertSequenceEqual(qs, [72])
|
self.assertSequenceEqual(qs, [72])
|
||||||
|
|
||||||
|
# Entire test can be removed once deprecation period ends.
|
||||||
|
@ignore_warnings(category=RemovedInDjango60Warning)
|
||||||
def test_extra_values(self):
|
def test_extra_values(self):
|
||||||
# testing for ticket 14930 issues
|
# testing for ticket 14930 issues
|
||||||
qs = Number.objects.extra(
|
qs = Number.objects.extra(
|
||||||
@ -2692,6 +2720,8 @@ class ValuesQuerysetTests(TestCase):
|
|||||||
qs = qs.values("num")
|
qs = qs.values("num")
|
||||||
self.assertSequenceEqual(qs, [{"num": 72}])
|
self.assertSequenceEqual(qs, [{"num": 72}])
|
||||||
|
|
||||||
|
# Entire test can be removed once deprecation period ends.
|
||||||
|
@ignore_warnings(category=RemovedInDjango60Warning)
|
||||||
def test_extra_values_order_twice(self):
|
def test_extra_values_order_twice(self):
|
||||||
# testing for ticket 14930 issues
|
# testing for ticket 14930 issues
|
||||||
qs = Number.objects.extra(
|
qs = Number.objects.extra(
|
||||||
@ -2701,6 +2731,8 @@ class ValuesQuerysetTests(TestCase):
|
|||||||
qs = qs.values("num")
|
qs = qs.values("num")
|
||||||
self.assertSequenceEqual(qs, [{"num": 72}])
|
self.assertSequenceEqual(qs, [{"num": 72}])
|
||||||
|
|
||||||
|
# Entire test can be removed once deprecation period ends.
|
||||||
|
@ignore_warnings(category=RemovedInDjango60Warning)
|
||||||
def test_extra_values_order_multiple(self):
|
def test_extra_values_order_multiple(self):
|
||||||
# Postgres doesn't allow constants in order by, so check for that.
|
# Postgres doesn't allow constants in order by, so check for that.
|
||||||
qs = Number.objects.extra(
|
qs = Number.objects.extra(
|
||||||
@ -2714,6 +2746,8 @@ class ValuesQuerysetTests(TestCase):
|
|||||||
qs = qs.values("num")
|
qs = qs.values("num")
|
||||||
self.assertSequenceEqual(qs, [{"num": 72}])
|
self.assertSequenceEqual(qs, [{"num": 72}])
|
||||||
|
|
||||||
|
# Entire test can be removed once deprecation period ends.
|
||||||
|
@ignore_warnings(category=RemovedInDjango60Warning)
|
||||||
def test_extra_values_order_in_extra(self):
|
def test_extra_values_order_in_extra(self):
|
||||||
# testing for ticket 14930 issues
|
# testing for ticket 14930 issues
|
||||||
qs = Number.objects.extra(
|
qs = Number.objects.extra(
|
||||||
@ -2722,6 +2756,8 @@ class ValuesQuerysetTests(TestCase):
|
|||||||
)
|
)
|
||||||
qs = qs.values("num")
|
qs = qs.values("num")
|
||||||
|
|
||||||
|
# Entire test can be removed once deprecation period ends.
|
||||||
|
@ignore_warnings(category=RemovedInDjango60Warning)
|
||||||
def test_extra_select_params_values_order_in_extra(self):
|
def test_extra_select_params_values_order_in_extra(self):
|
||||||
# testing for 23259 issue
|
# testing for 23259 issue
|
||||||
qs = Number.objects.extra(
|
qs = Number.objects.extra(
|
||||||
@ -2733,6 +2769,8 @@ class ValuesQuerysetTests(TestCase):
|
|||||||
qs = qs.values("num")
|
qs = qs.values("num")
|
||||||
self.assertSequenceEqual(qs, [{"num": 72}])
|
self.assertSequenceEqual(qs, [{"num": 72}])
|
||||||
|
|
||||||
|
# Entire test can be removed once deprecation period ends.
|
||||||
|
@ignore_warnings(category=RemovedInDjango60Warning)
|
||||||
def test_extra_multiple_select_params_values_order_by(self):
|
def test_extra_multiple_select_params_values_order_by(self):
|
||||||
# testing for 23259 issue
|
# testing for 23259 issue
|
||||||
qs = Number.objects.extra(
|
qs = Number.objects.extra(
|
||||||
@ -2744,6 +2782,8 @@ class ValuesQuerysetTests(TestCase):
|
|||||||
qs = qs.values("num")
|
qs = qs.values("num")
|
||||||
self.assertSequenceEqual(qs, [])
|
self.assertSequenceEqual(qs, [])
|
||||||
|
|
||||||
|
# Entire test can be removed once deprecation period ends.
|
||||||
|
@ignore_warnings(category=RemovedInDjango60Warning)
|
||||||
def test_extra_values_list(self):
|
def test_extra_values_list(self):
|
||||||
# testing for ticket 14930 issues
|
# testing for ticket 14930 issues
|
||||||
qs = Number.objects.extra(select={"value_plus_one": "num+1"})
|
qs = Number.objects.extra(select={"value_plus_one": "num+1"})
|
||||||
@ -2751,6 +2791,8 @@ class ValuesQuerysetTests(TestCase):
|
|||||||
qs = qs.values_list("num")
|
qs = qs.values_list("num")
|
||||||
self.assertSequenceEqual(qs, [(72,)])
|
self.assertSequenceEqual(qs, [(72,)])
|
||||||
|
|
||||||
|
# Entire test can be removed once deprecation period ends.
|
||||||
|
@ignore_warnings(category=RemovedInDjango60Warning)
|
||||||
def test_flat_extra_values_list(self):
|
def test_flat_extra_values_list(self):
|
||||||
# testing for ticket 14930 issues
|
# testing for ticket 14930 issues
|
||||||
qs = Number.objects.extra(select={"value_plus_one": "num+1"})
|
qs = Number.objects.extra(select={"value_plus_one": "num+1"})
|
||||||
@ -2772,6 +2814,8 @@ class ValuesQuerysetTests(TestCase):
|
|||||||
with self.assertRaisesMessage(TypeError, msg):
|
with self.assertRaisesMessage(TypeError, msg):
|
||||||
Number.objects.values_list("num", flat=True, named=True)
|
Number.objects.values_list("num", flat=True, named=True)
|
||||||
|
|
||||||
|
# Entire test can be removed once deprecation period ends.
|
||||||
|
@ignore_warnings(category=RemovedInDjango60Warning)
|
||||||
def test_named_values_list_bad_field_name(self):
|
def test_named_values_list_bad_field_name(self):
|
||||||
msg = "Type names and field names must be valid identifiers: '1'"
|
msg = "Type names and field names must be valid identifiers: '1'"
|
||||||
with self.assertRaisesMessage(ValueError, msg):
|
with self.assertRaisesMessage(ValueError, msg):
|
||||||
@ -2779,6 +2823,8 @@ class ValuesQuerysetTests(TestCase):
|
|||||||
"1", named=True
|
"1", named=True
|
||||||
).first()
|
).first()
|
||||||
|
|
||||||
|
# Entire test can be removed once deprecation period ends.
|
||||||
|
@ignore_warnings(category=RemovedInDjango60Warning)
|
||||||
def test_named_values_list_with_fields(self):
|
def test_named_values_list_with_fields(self):
|
||||||
qs = Number.objects.extra(select={"num2": "num+1"}).annotate(Count("id"))
|
qs = Number.objects.extra(select={"num2": "num+1"}).annotate(Count("id"))
|
||||||
values = qs.values_list("num", "num2", named=True).first()
|
values = qs.values_list("num", "num2", named=True).first()
|
||||||
@ -2787,13 +2833,15 @@ class ValuesQuerysetTests(TestCase):
|
|||||||
self.assertEqual(values.num, 72)
|
self.assertEqual(values.num, 72)
|
||||||
self.assertEqual(values.num2, 73)
|
self.assertEqual(values.num2, 73)
|
||||||
|
|
||||||
|
# Entire test can be removed once deprecation period ends.
|
||||||
|
@ignore_warnings(category=RemovedInDjango60Warning)
|
||||||
def test_named_values_list_without_fields(self):
|
def test_named_values_list_without_fields(self):
|
||||||
qs = Number.objects.extra(select={"num2": "num+1"}).annotate(Count("id"))
|
qs = Number.objects.extra(select={"num2": "num+1"}).annotate(Count("id"))
|
||||||
values = qs.values_list(named=True).first()
|
values = qs.values_list(named=True).first()
|
||||||
self.assertEqual(type(values).__name__, "Row")
|
self.assertEqual(type(values).__name__, "Row")
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
values._fields,
|
values._fields,
|
||||||
("num2", "id", "num", "other_num", "another_num", "id__count"),
|
("id", "num", "other_num", "another_num", "num2", "id__count"),
|
||||||
)
|
)
|
||||||
self.assertEqual(values.num, 72)
|
self.assertEqual(values.num, 72)
|
||||||
self.assertEqual(values.num2, 73)
|
self.assertEqual(values.num2, 73)
|
||||||
@ -2980,6 +3028,8 @@ class WeirdQuerysetSlicingTests(TestCase):
|
|||||||
self.assertQuerySetEqual(Article.objects.values_list()[n:n], [])
|
self.assertQuerySetEqual(Article.objects.values_list()[n:n], [])
|
||||||
|
|
||||||
|
|
||||||
|
# Entire testcase can be removed once deprecation period ends.
|
||||||
|
@ignore_warnings(category=RemovedInDjango60Warning)
|
||||||
class EscapingTests(TestCase):
|
class EscapingTests(TestCase):
|
||||||
def test_ticket_7302(self):
|
def test_ticket_7302(self):
|
||||||
# Reserved names are appropriately escaped
|
# Reserved names are appropriately escaped
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
from django.core.exceptions import FieldError
|
from django.core.exceptions import FieldError
|
||||||
from django.test import SimpleTestCase, TestCase
|
from django.test import SimpleTestCase, TestCase, ignore_warnings
|
||||||
|
from django.utils.deprecation import RemovedInDjango60Warning
|
||||||
|
|
||||||
from .models import (
|
from .models import (
|
||||||
Bookmark,
|
Bookmark,
|
||||||
@ -119,6 +120,8 @@ class SelectRelatedTests(TestCase):
|
|||||||
sorted(orders), ["Agaricales", "Diptera", "Fabales", "Primates"]
|
sorted(orders), ["Agaricales", "Diptera", "Fabales", "Primates"]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Entire test can be removed once deprecation period ends.
|
||||||
|
@ignore_warnings(category=RemovedInDjango60Warning)
|
||||||
def test_select_related_with_extra(self):
|
def test_select_related_with_extra(self):
|
||||||
s = (
|
s = (
|
||||||
Species.objects.all()
|
Species.objects.all()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user