diff options
| author | Anssi Kääriäinen <akaariai@gmail.com> | 2014-11-18 11:24:33 +0200 |
|---|---|---|
| committer | Tim Graham <timograham@gmail.com> | 2014-11-26 17:49:25 -0500 |
| commit | cbb5cdd155668ba771cad6b975676d3b20fed37b (patch) | |
| tree | a4eeafd6853352561cc5e04b0d500197eb81a9fe /django/db/models/sql | |
| parent | cc870b8ef5e3464c6f051e3ef0a25dfc4b597452 (diff) | |
Fixed #23867 -- removed DateQuerySet hacks
The .dates() queries were implemented by using custom Query, QuerySet,
and Compiler classes. Instead implement them by using expressions and
database converters APIs.
Diffstat (limited to 'django/db/models/sql')
| -rw-r--r-- | django/db/models/sql/compiler.py | 67 | ||||
| -rw-r--r-- | django/db/models/sql/query.py | 3 | ||||
| -rw-r--r-- | django/db/models/sql/subqueries.py | 79 |
3 files changed, 10 insertions, 139 deletions
diff --git a/django/db/models/sql/compiler.py b/django/db/models/sql/compiler.py index 4825fae3fa..b800c3fc3e 100644 --- a/django/db/models/sql/compiler.py +++ b/django/db/models/sql/compiler.py @@ -1,7 +1,5 @@ -import datetime import warnings -from django.conf import settings from django.core.exceptions import FieldError from django.db.backends.utils import truncate_name from django.db.models.constants import LOOKUP_SEP @@ -13,7 +11,6 @@ from django.db.models.sql.query import get_order_dir, Query from django.db.transaction import TransactionManagementError from django.db.utils import DatabaseError from django.utils import six -from django.utils import timezone from django.utils.deprecation import RemovedInDjango20Warning from django.utils.six.moves import zip @@ -698,10 +695,14 @@ class SQLCompiler(object): index_extra_select = len(self.query.extra_select) for i, field in enumerate(fields): if field: - backend_converters = self.connection.ops.get_db_converters(field.get_internal_type()) + try: + output_field = field.output_field + except AttributeError: + output_field = field + backend_converters = self.connection.ops.get_db_converters(output_field.get_internal_type()) field_converters = field.get_db_converters(self.connection) if backend_converters or field_converters: - converters[index_extra_select + i] = (backend_converters, field_converters, field) + converters[index_extra_select + i] = (backend_converters, field_converters, output_field) return converters def apply_converters(self, row, converters): @@ -753,11 +754,8 @@ class SQLCompiler(object): # annotations come before the related cols if has_annotation_select: # extra is always at the start of the field list - prepended_cols = len(self.query.extra_select) - annotation_start = len(fields) + prepended_cols fields = fields + [ - anno.output_field for alias, anno in self.query.annotation_select.items()] - annotation_end = len(fields) + prepended_cols + anno for alias, anno in self.query.annotation_select.items()] # add related fields fields = fields + [ @@ -768,16 +766,6 @@ class SQLCompiler(object): ] converters = self.get_converters(fields) - if has_annotation_select: - for (alias, annotation), position in zip( - self.query.annotation_select.items(), - range(annotation_start, annotation_end + 1)): - if position in converters: - # annotation conversions always run first - converters[position][1].insert(0, annotation.convert_value) - else: - converters[position] = ([], [annotation.convert_value], annotation.output_field) - if converters: row = self.apply_converters(row, converters) yield row @@ -1122,47 +1110,6 @@ class SQLAggregateCompiler(SQLCompiler): return sql, params -class SQLDateCompiler(SQLCompiler): - def results_iter(self): - """ - Returns an iterator over the results from executing this query. - """ - from django.db.models.fields import DateField - converters = self.get_converters([DateField()]) - - offset = len(self.query.extra_select) - for rows in self.execute_sql(MULTI): - for row in rows: - date = self.apply_converters(row, converters)[offset] - if isinstance(date, datetime.datetime): - date = date.date() - yield date - - -class SQLDateTimeCompiler(SQLCompiler): - def results_iter(self): - """ - Returns an iterator over the results from executing this query. - """ - from django.db.models.fields import DateTimeField - converters = self.get_converters([DateTimeField()]) - - offset = len(self.query.extra_select) - for rows in self.execute_sql(MULTI): - for row in rows: - datetime = self.apply_converters(row, converters)[offset] - # Datetimes are artificially returned in UTC on databases that - # don't support time zone. Restore the zone used in the query. - if settings.USE_TZ: - if datetime is None: - raise ValueError("Database returned an invalid value " - "in QuerySet.datetimes(). Are time zone " - "definitions for your database and pytz installed?") - datetime = datetime.replace(tzinfo=None) - datetime = timezone.make_aware(datetime, self.query.tzinfo) - yield datetime - - def cursor_iter(cursor, sentinel): """ Yields blocks of rows from a cursor and ensures the cursor is closed when diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py index 4702bc1945..a5d067a37b 100644 --- a/django/db/models/sql/query.py +++ b/django/db/models/sql/query.py @@ -992,7 +992,8 @@ class Query(object): """ Adds a single annotation expression to the Query """ - annotation = annotation.resolve_expression(self, summarize=is_summary) + annotation = annotation.resolve_expression(self, allow_joins=True, reuse=None, + summarize=is_summary) self.append_annotation_mask([alias]) self.annotations[alias] = annotation diff --git a/django/db/models/sql/subqueries.py b/django/db/models/sql/subqueries.py index 6f3f7358d3..12bde13bf3 100644 --- a/django/db/models/sql/subqueries.py +++ b/django/db/models/sql/subqueries.py @@ -2,21 +2,15 @@ Query subclasses which provide extra functionality beyond simple data retrieval. """ -from django.conf import settings from django.core.exceptions import FieldError from django.db import connections from django.db.models.query_utils import Q -from django.db.models.constants import LOOKUP_SEP -from django.db.models.expressions import Date, DateTime, Col -from django.db.models.fields import DateField, DateTimeField, FieldDoesNotExist from django.db.models.sql.constants import GET_ITERATOR_CHUNK_SIZE, NO_RESULTS, SelectInfo from django.db.models.sql.query import Query from django.utils import six -from django.utils import timezone -__all__ = ['DeleteQuery', 'UpdateQuery', 'InsertQuery', 'DateQuery', - 'DateTimeQuery', 'AggregateQuery'] +__all__ = ['DeleteQuery', 'UpdateQuery', 'InsertQuery', 'AggregateQuery'] class DeleteQuery(Query): @@ -204,77 +198,6 @@ class InsertQuery(Query): self.raw = raw -class DateQuery(Query): - """ - A DateQuery is a normal query, except that it specifically selects a single - date field. This requires some special handling when converting the results - back to Python objects, so we put it in a separate class. - """ - - compiler = 'SQLDateCompiler' - - def add_select(self, field_name, lookup_type, order='ASC'): - """ - Converts the query into an extraction query. - """ - try: - field, _, _, joins, _ = self.setup_joins( - field_name.split(LOOKUP_SEP), - self.get_meta(), - self.get_initial_alias(), - ) - except FieldError: - raise FieldDoesNotExist("%s has no field named '%s'" % ( - self.get_meta().object_name, field_name - )) - self._check_field(field) # overridden in DateTimeQuery - alias = joins[-1] - select = self._get_select(Col(alias, field), lookup_type) - self.clear_select_clause() - self.select = [SelectInfo(select, None)] - self.distinct = True - self.order_by = [1] if order == 'ASC' else [-1] - - if field.null: - self.add_filter(("%s__isnull" % field_name, False)) - - def _check_field(self, field): - assert isinstance(field, DateField), \ - "%r isn't a DateField." % field.name - if settings.USE_TZ: - assert not isinstance(field, DateTimeField), \ - "%r is a DateTimeField, not a DateField." % field.name - - def _get_select(self, col, lookup_type): - return Date(col, lookup_type) - - -class DateTimeQuery(DateQuery): - """ - A DateTimeQuery is like a DateQuery but for a datetime field. If time zone - support is active, the tzinfo attribute contains the time zone to use for - converting the values before truncating them. Otherwise it's set to None. - """ - - compiler = 'SQLDateTimeCompiler' - - def clone(self, klass=None, memo=None, **kwargs): - if 'tzinfo' not in kwargs and hasattr(self, 'tzinfo'): - kwargs['tzinfo'] = self.tzinfo - return super(DateTimeQuery, self).clone(klass, memo, **kwargs) - - def _check_field(self, field): - assert isinstance(field, DateTimeField), \ - "%r isn't a DateTimeField." % field.name - - def _get_select(self, col, lookup_type): - if self.tzinfo is None: - tzname = None - else: - tzname = timezone._get_timezone_name(self.tzinfo) - return DateTime(col, lookup_type, tzname) - - class AggregateQuery(Query): """ An AggregateQuery takes another query as a parameter to the FROM |
