Refs #14030 -- Removed backwards compatiblity for old-style aggregates.
Per deprecation timeline.
This commit is contained in:
parent
7140d4adf7
commit
fd6a299cd0
|
@ -1,10 +0,0 @@
|
|||
from django.db.models.sql import aggregates
|
||||
from django.db.models.sql.aggregates import * # NOQA
|
||||
|
||||
__all__ = ['Collect', 'Extent', 'Extent3D', 'MakeLine', 'Union'] + aggregates.__all__
|
||||
|
||||
|
||||
warnings.warn(
|
||||
"django.contrib.gis.db.models.sql.aggregates is deprecated. Use "
|
||||
"django.contrib.gis.db.models.aggregates instead.",
|
||||
RemovedInDjango110Warning, stacklevel=2)
|
|
@ -1,155 +0,0 @@
|
|||
"""
|
||||
Classes to represent the default SQL aggregate functions
|
||||
"""
|
||||
import copy
|
||||
import warnings
|
||||
|
||||
from django.db.models.fields import FloatField, IntegerField
|
||||
from django.db.models.query_utils import RegisterLookupMixin
|
||||
from django.utils.deprecation import RemovedInDjango110Warning
|
||||
from django.utils.functional import cached_property
|
||||
|
||||
__all__ = ['Aggregate', 'Avg', 'Count', 'Max', 'Min', 'StdDev', 'Sum', 'Variance']
|
||||
|
||||
|
||||
warnings.warn(
|
||||
"django.db.models.sql.aggregates is deprecated. Use "
|
||||
"django.db.models.aggregates instead.",
|
||||
RemovedInDjango110Warning, stacklevel=2)
|
||||
|
||||
|
||||
class Aggregate(RegisterLookupMixin):
|
||||
"""
|
||||
Default SQL Aggregate.
|
||||
"""
|
||||
is_ordinal = False
|
||||
is_computed = False
|
||||
sql_template = '%(function)s(%(field)s)'
|
||||
|
||||
def __init__(self, col, source=None, is_summary=False, **extra):
|
||||
"""Instantiate an SQL aggregate
|
||||
|
||||
* col is a column reference describing the subject field
|
||||
of the aggregate. It can be an alias, or a tuple describing
|
||||
a table and column name.
|
||||
* source is the underlying field or aggregate definition for
|
||||
the column reference. If the aggregate is not an ordinal or
|
||||
computed type, this reference is used to determine the coerced
|
||||
output type of the aggregate.
|
||||
* extra is a dictionary of additional data to provide for the
|
||||
aggregate definition
|
||||
|
||||
Also utilizes the class variables:
|
||||
* sql_function, the name of the SQL function that implements the
|
||||
aggregate.
|
||||
* sql_template, a template string that is used to render the
|
||||
aggregate into SQL.
|
||||
* is_ordinal, a boolean indicating if the output of this aggregate
|
||||
is an integer (e.g., a count)
|
||||
* is_computed, a boolean indicating if this output of this aggregate
|
||||
is a computed float (e.g., an average), regardless of the input
|
||||
type.
|
||||
"""
|
||||
self.col = col
|
||||
self.source = source
|
||||
self.is_summary = is_summary
|
||||
self.extra = extra
|
||||
|
||||
# Follow the chain of aggregate sources back until you find an
|
||||
# actual field, or an aggregate that forces a particular output
|
||||
# type. This type of this field will be used to coerce values
|
||||
# retrieved from the database.
|
||||
tmp = self
|
||||
|
||||
while tmp and isinstance(tmp, Aggregate):
|
||||
if getattr(tmp, 'is_ordinal', False):
|
||||
tmp = self._ordinal_aggregate_field
|
||||
elif getattr(tmp, 'is_computed', False):
|
||||
tmp = self._computed_aggregate_field
|
||||
else:
|
||||
tmp = tmp.source
|
||||
|
||||
self.field = tmp
|
||||
|
||||
# Two fake fields used to identify aggregate types in data-conversion operations.
|
||||
@cached_property
|
||||
def _ordinal_aggregate_field(self):
|
||||
return IntegerField()
|
||||
|
||||
@cached_property
|
||||
def _computed_aggregate_field(self):
|
||||
return FloatField()
|
||||
|
||||
def relabeled_clone(self, change_map):
|
||||
clone = copy.copy(self)
|
||||
if isinstance(self.col, (list, tuple)):
|
||||
clone.col = (change_map.get(self.col[0], self.col[0]), self.col[1])
|
||||
return clone
|
||||
|
||||
def as_sql(self, compiler, connection):
|
||||
"Return the aggregate, rendered as SQL with parameters."
|
||||
params = []
|
||||
|
||||
if hasattr(self.col, 'as_sql'):
|
||||
field_name, params = self.col.as_sql(compiler, connection)
|
||||
elif isinstance(self.col, (list, tuple)):
|
||||
field_name = '.'.join(compiler(c) for c in self.col)
|
||||
else:
|
||||
field_name = compiler(self.col)
|
||||
|
||||
substitutions = {
|
||||
'function': self.sql_function,
|
||||
'field': field_name
|
||||
}
|
||||
substitutions.update(self.extra)
|
||||
|
||||
return self.sql_template % substitutions, params
|
||||
|
||||
def get_group_by_cols(self):
|
||||
return []
|
||||
|
||||
@property
|
||||
def output_field(self):
|
||||
return self.field
|
||||
|
||||
|
||||
class Avg(Aggregate):
|
||||
is_computed = True
|
||||
sql_function = 'AVG'
|
||||
|
||||
|
||||
class Count(Aggregate):
|
||||
is_ordinal = True
|
||||
sql_function = 'COUNT'
|
||||
sql_template = '%(function)s(%(distinct)s%(field)s)'
|
||||
|
||||
def __init__(self, col, distinct=False, **extra):
|
||||
super(Count, self).__init__(col, distinct='DISTINCT ' if distinct else '', **extra)
|
||||
|
||||
|
||||
class Max(Aggregate):
|
||||
sql_function = 'MAX'
|
||||
|
||||
|
||||
class Min(Aggregate):
|
||||
sql_function = 'MIN'
|
||||
|
||||
|
||||
class StdDev(Aggregate):
|
||||
is_computed = True
|
||||
|
||||
def __init__(self, col, sample=False, **extra):
|
||||
super(StdDev, self).__init__(col, **extra)
|
||||
self.sql_function = 'STDDEV_SAMP' if sample else 'STDDEV_POP'
|
||||
|
||||
|
||||
class Sum(Aggregate):
|
||||
sql_function = 'SUM'
|
||||
|
||||
|
||||
class Variance(Aggregate):
|
||||
is_computed = True
|
||||
|
||||
def __init__(self, col, sample=False, **extra):
|
||||
super(Variance, self).__init__(col, **extra)
|
||||
self.sql_function = 'VAR_SAMP' if sample else 'VAR_POP'
|
|
@ -7,7 +7,6 @@ databases). The abstraction barrier only works one way: this module has to know
|
|||
all about the internals of models in order to get the information it needs.
|
||||
"""
|
||||
import copy
|
||||
import warnings
|
||||
from collections import Counter, Iterator, Mapping, OrderedDict
|
||||
from itertools import chain, count, product
|
||||
from string import ascii_uppercase
|
||||
|
@ -31,7 +30,6 @@ from django.db.models.sql.where import (
|
|||
AND, OR, ExtraWhere, NothingNode, WhereNode,
|
||||
)
|
||||
from django.utils import six
|
||||
from django.utils.deprecation import RemovedInDjango110Warning
|
||||
from django.utils.encoding import force_text
|
||||
from django.utils.tree import Node
|
||||
|
||||
|
@ -214,13 +212,6 @@ class Query(object):
|
|||
self._annotations = OrderedDict()
|
||||
return self._annotations
|
||||
|
||||
@property
|
||||
def aggregates(self):
|
||||
warnings.warn(
|
||||
"The aggregates property is deprecated. Use annotations instead.",
|
||||
RemovedInDjango110Warning, stacklevel=2)
|
||||
return self.annotations
|
||||
|
||||
def __str__(self):
|
||||
"""
|
||||
Returns the query as a string of SQL with the parameter values
|
||||
|
@ -973,12 +964,6 @@ class Query(object):
|
|||
alias = seen[int_model] = joins[-1]
|
||||
return alias or seen[None]
|
||||
|
||||
def add_aggregate(self, aggregate, model, alias, is_summary):
|
||||
warnings.warn(
|
||||
"add_aggregate() is deprecated. Use add_annotation() instead.",
|
||||
RemovedInDjango110Warning, stacklevel=2)
|
||||
self.add_annotation(aggregate, alias, is_summary)
|
||||
|
||||
def add_annotation(self, annotation, alias, is_summary=False):
|
||||
"""
|
||||
Adds a single annotation expression to the Query
|
||||
|
@ -1818,12 +1803,6 @@ class Query(object):
|
|||
"""
|
||||
target[model] = {f.attname for f in fields}
|
||||
|
||||
def set_aggregate_mask(self, names):
|
||||
warnings.warn(
|
||||
"set_aggregate_mask() is deprecated. Use set_annotation_mask() instead.",
|
||||
RemovedInDjango110Warning, stacklevel=2)
|
||||
self.set_annotation_mask(names)
|
||||
|
||||
def set_annotation_mask(self, names):
|
||||
"Set the mask of annotations that will actually be returned by the SELECT"
|
||||
if names is None:
|
||||
|
@ -1832,12 +1811,6 @@ class Query(object):
|
|||
self.annotation_select_mask = set(names)
|
||||
self._annotation_select_cache = None
|
||||
|
||||
def append_aggregate_mask(self, names):
|
||||
warnings.warn(
|
||||
"append_aggregate_mask() is deprecated. Use append_annotation_mask() instead.",
|
||||
RemovedInDjango110Warning, stacklevel=2)
|
||||
self.append_annotation_mask(names)
|
||||
|
||||
def append_annotation_mask(self, names):
|
||||
if self.annotation_select_mask is not None:
|
||||
self.set_annotation_mask(set(names).union(self.annotation_select_mask))
|
||||
|
@ -1874,13 +1847,6 @@ class Query(object):
|
|||
else:
|
||||
return self.annotations
|
||||
|
||||
@property
|
||||
def aggregate_select(self):
|
||||
warnings.warn(
|
||||
"aggregate_select() is deprecated. Use annotation_select() instead.",
|
||||
RemovedInDjango110Warning, stacklevel=2)
|
||||
return self.annotation_select
|
||||
|
||||
@property
|
||||
def extra_select(self):
|
||||
if self._extra_select_cache is not None:
|
||||
|
|
|
@ -7,13 +7,12 @@ from decimal import Decimal
|
|||
from django.core.exceptions import FieldError
|
||||
from django.db import connection
|
||||
from django.db.models import (
|
||||
F, Aggregate, Avg, Count, DecimalField, DurationField, FloatField, Func,
|
||||
IntegerField, Max, Min, Sum, Value,
|
||||
F, Avg, Count, DecimalField, DurationField, FloatField, Func, IntegerField,
|
||||
Max, Min, Sum, Value,
|
||||
)
|
||||
from django.test import TestCase, ignore_warnings
|
||||
from django.test import TestCase
|
||||
from django.test.utils import Approximate, CaptureQueriesContext
|
||||
from django.utils import six, timezone
|
||||
from django.utils.deprecation import RemovedInDjango110Warning
|
||||
|
||||
from .models import Author, Book, Publisher, Store
|
||||
|
||||
|
@ -1184,23 +1183,3 @@ class AggregateTestCase(TestCase):
|
|||
).filter(rating_or_num_awards__gt=F('num_awards')).order_by('num_awards')
|
||||
self.assertQuerysetEqual(
|
||||
qs2, [1, 3], lambda v: v.num_awards)
|
||||
|
||||
@ignore_warnings(category=RemovedInDjango110Warning)
|
||||
def test_backwards_compatibility(self):
|
||||
from django.db.models.sql import aggregates as sql_aggregates
|
||||
|
||||
class SqlNewSum(sql_aggregates.Aggregate):
|
||||
sql_function = 'SUM'
|
||||
|
||||
class NewSum(Aggregate):
|
||||
name = 'Sum'
|
||||
|
||||
def add_to_query(self, query, alias, col, source, is_summary):
|
||||
klass = SqlNewSum
|
||||
aggregate = klass(
|
||||
col, source=source, is_summary=is_summary, **self.extra)
|
||||
query.annotations[alias] = aggregate
|
||||
|
||||
qs = Author.objects.values('name').annotate(another_age=NewSum('age') + F('age'))
|
||||
a = qs.get(name="Adrian Holovaty")
|
||||
self.assertEqual(a['another_age'], 68)
|
||||
|
|
Loading…
Reference in New Issue