From 5008a4db440c8f7d108a6979b959025ffb5789ba Mon Sep 17 00:00:00 2001 From: Tim Graham Date: Sun, 18 Jan 2015 14:29:09 -0500 Subject: [PATCH] Removed legacy ORM lookup support per deprecation timeline; refs #16187. --- django/db/models/sql/query.py | 21 +-- django/db/models/sql/where.py | 234 +--------------------------------- django/utils/tree.py | 8 -- 3 files changed, 8 insertions(+), 255 deletions(-) diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py index ace562a8fa500..94b82dbe790b0 100644 --- a/django/db/models/sql/query.py +++ b/django/db/models/sql/query.py @@ -24,7 +24,7 @@ ORDER_PATTERN, INNER, LOUTER) from django.db.models.sql.datastructures import ( EmptyResultSet, Empty, MultiJoin, Join, BaseTable) -from django.db.models.sql.where import (WhereNode, Constraint, EverythingNode, +from django.db.models.sql.where import (WhereNode, EverythingNode, ExtraWhere, AND, OR, EmptyWhere) from django.utils import six from django.utils.deprecation import RemovedInDjango20Warning @@ -1130,10 +1130,6 @@ def build_filter(self, filter_expr, branch_negated=False, current_negated=False, clause = self.where_class() if reffed_aggregate: condition = self.build_lookup(lookups, reffed_aggregate, value) - if not condition: - # Backwards compat for custom lookups - assert len(lookups) == 1 - condition = (reffed_aggregate, lookups[0], value) clause.add(condition, AND) return clause, [] @@ -1176,20 +1172,7 @@ def build_filter(self, filter_expr, branch_negated=False, current_negated=False, else: col = targets[0].get_col(alias, field) condition = self.build_lookup(lookups, col, value) - if not condition: - # Backwards compat for custom lookups - if lookups[0] not in self.query_terms: - raise FieldError( - "Join on field '%s' not permitted. Did you " - "misspell '%s' for the lookup type?" % - (col.output_field.name, lookups[0])) - if len(lookups) > 1: - raise FieldError("Nested lookup '%s' not supported." % - LOOKUP_SEP.join(lookups)) - condition = (Constraint(alias, targets[0].column, field), lookups[0], value) - lookup_type = lookups[-1] - else: - lookup_type = condition.lookup_name + lookup_type = condition.lookup_name clause.add(condition, AND) diff --git a/django/db/models/sql/where.py b/django/db/models/sql/where.py index e3766c51d6253..3475ccaee5bf4 100644 --- a/django/db/models/sql/where.py +++ b/django/db/models/sql/where.py @@ -1,19 +1,8 @@ """ Code to manage the creation and SQL rendering of 'where' constraints. """ - -import collections -import datetime -from itertools import repeat -import warnings - -from django.conf import settings -from django.db.models.fields import DateTimeField, Field -from django.db.models.sql.datastructures import EmptyResultSet, Empty -from django.utils.deprecation import RemovedInDjango19Warning +from django.db.models.sql.datastructures import EmptyResultSet from django.utils.functional import cached_property -from django.utils.six.moves import range -from django.utils import timezone from django.utils import tree @@ -37,10 +26,9 @@ class WhereNode(tree.Node): The class is tied to the Query class that created it (in order to create the correct SQL). - A child is usually a tuple of: - (Constraint(alias, targetcol, field), lookup_type, value) - where value can be either raw Python value, or Query, ExpressionNode or - something else knowing how to turn itself into SQL. + A child is usually an expression producing boolean values. Most likely the + expression is a Lookup instance, but other types of objects fulfilling the + required API could be used too (for example, sql.where.EverythingNode). However, a child could also be any class with as_sql() and either relabeled_clone() method or relabel_aliases() and clone() methods. The @@ -49,39 +37,6 @@ class WhereNode(tree.Node): """ default = AND - def _prepare_data(self, data): - """ - Prepare data for addition to the tree. If the data is a list or tuple, - it is expected to be of the form (obj, lookup_type, value), where obj - is a Constraint object, and is then slightly munged before being - stored (to avoid storing any reference to field objects). Otherwise, - the 'data' is stored unchanged and can be any class with an 'as_sql()' - method. - """ - if not isinstance(data, (list, tuple)): - return data - obj, lookup_type, value = data - if isinstance(value, collections.Iterator): - # Consume any generators immediately, so that we can determine - # emptiness and transform any non-empty values correctly. - value = list(value) - - # The "value_annotation" parameter is used to pass auxiliary information - # about the value(s) to the query construction. Specifically, datetime - # and empty values need special handling. Other types could be used - # here in the future (using Python types is suggested for consistency). - if (isinstance(value, datetime.datetime) - or (isinstance(obj.field, DateTimeField) and lookup_type != 'isnull')): - value_annotation = datetime.datetime - elif hasattr(value, 'value_annotation'): - value_annotation = value.value_annotation - else: - value_annotation = bool(value) - - if hasattr(obj, 'prepare'): - value = obj.prepare(lookup_type, value) - return (obj, lookup_type, value_annotation, value) - def as_sql(self, compiler, connection): """ Returns the SQL version of the where clause and the value to be @@ -102,11 +57,7 @@ def as_sql(self, compiler, connection): for child in self.children: try: - if hasattr(child, 'as_sql'): - sql, params = compiler.compile(child) - else: - # A leaf node in the tree. - sql, params = self.make_atom(child, compiler, connection) + sql, params = compiler.compile(child) except EmptyResultSet: nothing_childs += 1 else: @@ -157,125 +108,9 @@ def as_sql(self, compiler, connection): def get_group_by_cols(self): cols = [] for child in self.children: - if hasattr(child, 'get_group_by_cols'): - cols.extend(child.get_group_by_cols()) - else: - if isinstance(child[0], Constraint): - cols.append((child[0].alias, child[0].col)) - if hasattr(child[3], 'get_group_by_cols'): - cols.extend(child[3].get_group_by_cols()) + cols.extend(child.get_group_by_cols()) return cols - def make_atom(self, child, compiler, connection): - """ - Turn a tuple (Constraint(table_alias, column_name, db_type), - lookup_type, value_annotation, params) into valid SQL. - - The first item of the tuple may also be an Aggregate. - - Returns the string for the SQL fragment and the parameters to use for - it. - """ - warnings.warn( - "The make_atom() method will be removed in Django 1.9. Use Lookup class instead.", - RemovedInDjango19Warning) - lvalue, lookup_type, value_annotation, params_or_value = child - field_internal_type = lvalue.field.get_internal_type() if lvalue.field else None - - if isinstance(lvalue, Constraint): - try: - lvalue, params = lvalue.process(lookup_type, params_or_value, connection) - except EmptyShortCircuit: - raise EmptyResultSet - else: - raise TypeError("'make_atom' expects a Constraint as the first " - "item of its 'child' argument.") - - if isinstance(lvalue, tuple): - # A direct database column lookup. - field_sql, field_params = self.sql_for_columns(lvalue, compiler, connection, field_internal_type), [] - else: - # A smart object with an as_sql() method. - field_sql, field_params = compiler.compile(lvalue) - - is_datetime_field = value_annotation is datetime.datetime - cast_sql = connection.ops.datetime_cast_sql() if is_datetime_field else '%s' - - if hasattr(params, 'as_sql'): - extra, params = compiler.compile(params) - cast_sql = '' - else: - extra = '' - - params = field_params + params - - if (len(params) == 1 and params[0] == '' and lookup_type == 'exact' - and connection.features.interprets_empty_strings_as_nulls): - lookup_type = 'isnull' - value_annotation = True - - if lookup_type in connection.operators: - format = "%s %%s %%s" % (connection.ops.lookup_cast(lookup_type),) - return (format % (field_sql, - connection.operators[lookup_type] % cast_sql, - extra), params) - - if lookup_type == 'in': - if not value_annotation: - raise EmptyResultSet - if extra: - return ('%s IN %s' % (field_sql, extra), params) - max_in_list_size = connection.ops.max_in_list_size() - if max_in_list_size and len(params) > max_in_list_size: - # Break up the params list into an OR of manageable chunks. - in_clause_elements = ['('] - for offset in range(0, len(params), max_in_list_size): - if offset > 0: - in_clause_elements.append(' OR ') - in_clause_elements.append('%s IN (' % field_sql) - group_size = min(len(params) - offset, max_in_list_size) - param_group = ', '.join(repeat('%s', group_size)) - in_clause_elements.append(param_group) - in_clause_elements.append(')') - in_clause_elements.append(')') - return ''.join(in_clause_elements), params - else: - return ('%s IN (%s)' % (field_sql, - ', '.join(repeat('%s', len(params)))), - params) - elif lookup_type in ('range', 'year'): - return ('%s BETWEEN %%s and %%s' % field_sql, params) - elif is_datetime_field and lookup_type in ('month', 'day', 'week_day', - 'hour', 'minute', 'second'): - tzname = timezone.get_current_timezone_name() if settings.USE_TZ else None - sql, tz_params = connection.ops.datetime_extract_sql(lookup_type, field_sql, tzname) - return ('%s = %%s' % sql, tz_params + params) - elif lookup_type in ('month', 'day', 'week_day'): - return ('%s = %%s' - % connection.ops.date_extract_sql(lookup_type, field_sql), params) - elif lookup_type == 'isnull': - assert value_annotation in (True, False), "Invalid value_annotation for isnull" - return ('%s IS %sNULL' % (field_sql, ('' if value_annotation else 'NOT ')), ()) - elif lookup_type == 'search': - return (connection.ops.fulltext_search_sql(field_sql), params) - elif lookup_type in ('regex', 'iregex'): - return connection.ops.regex_lookup(lookup_type) % (field_sql, cast_sql), params - - raise TypeError('Invalid lookup_type: %r' % lookup_type) - - def sql_for_columns(self, data, qn, connection, internal_type=None): - """ - Returns the SQL fragment used for the left-hand side of a column - constraint (for example, the "T1.foo" portion in the clause - "WHERE ... T1.foo = 6") and a list of parameters. - """ - table_alias, name, db_type = data - if table_alias: - lhs = '%s.%s' % (qn(table_alias), qn(name)) - else: - lhs = qn(name) - return connection.ops.field_cast_sql(db_type, internal_type) % lhs - def relabel_aliases(self, change_map): """ Relabels the alias values of any children. 'change_map' is a dictionary @@ -287,13 +122,6 @@ def relabel_aliases(self, change_map): child.relabel_aliases(change_map) elif hasattr(child, 'relabeled_clone'): self.children[pos] = child.relabeled_clone(change_map) - elif isinstance(child, (list, tuple)): - # tuple starting with Constraint - child = (child[0].relabeled_clone(change_map),) + child[1:] - if hasattr(child[3], 'relabeled_clone'): - child = (child[0], child[1], child[2]) + ( - child[3].relabeled_clone(change_map),) - self.children[pos] = child def clone(self): """ @@ -372,56 +200,6 @@ def as_sql(self, compiler=None, connection=None): return " AND ".join(sqls), list(self.params or ()) -class Constraint(object): - """ - An object that can be passed to WhereNode.add() and knows how to - pre-process itself prior to including in the WhereNode. - """ - def __init__(self, alias, col, field): - warnings.warn( - "The Constraint class will be removed in Django 1.9. Use Lookup class instead.", - RemovedInDjango19Warning) - self.alias, self.col, self.field = alias, col, field - - def prepare(self, lookup_type, value): - if self.field and not hasattr(value, 'as_sql'): - return self.field.get_prep_lookup(lookup_type, value) - return value - - def process(self, lookup_type, value, connection): - """ - Returns a tuple of data suitable for inclusion in a WhereNode - instance. - """ - # Because of circular imports, we need to import this here. - from django.db.models.base import ObjectDoesNotExist - try: - if self.field: - params = self.field.get_db_prep_lookup(lookup_type, value, - connection=connection, prepared=True) - db_type = self.field.db_type(connection=connection) - else: - # This branch is used at times when we add a comparison to NULL - # (we don't really want to waste time looking up the associated - # field object at the calling location). - params = Field().get_db_prep_lookup(lookup_type, value, - connection=connection, prepared=True) - db_type = None - except ObjectDoesNotExist: - raise EmptyShortCircuit - - return (self.alias, self.col, db_type), params - - def relabeled_clone(self, change_map): - if self.alias not in change_map: - return self - else: - new = Empty() - new.__class__ = self.__class__ - new.alias, new.col, new.field = change_map[self.alias], self.col, self.field - return new - - class SubqueryConstraint(object): def __init__(self, alias, columns, targets, query_object): self.alias = alias diff --git a/django/utils/tree.py b/django/utils/tree.py index a8d96893b0e24..9eb3aeb5d6031 100644 --- a/django/utils/tree.py +++ b/django/utils/tree.py @@ -81,13 +81,6 @@ def __contains__(self, other): """ return other in self.children - def _prepare_data(self, data): - """ - A subclass hook for doing subclass specific transformations of the - given data on combine() or add(). - """ - return data - def add(self, data, conn_type, squash=True): """ Combines this tree and the data represented by data using the @@ -105,7 +98,6 @@ def add(self, data, conn_type, squash=True): """ if data in self.children: return data - data = self._prepare_data(data) if not squash: self.children.append(data) return data