diff options
| author | MichaĆ Modzelewski <michal.modzelewski@gmail.com> | 2015-01-02 02:39:31 +0100 |
|---|---|---|
| committer | Tim Graham <timograham@gmail.com> | 2015-01-12 18:15:34 -0500 |
| commit | 65246de7b1d70d25831ab394c4f4a75813f629fe (patch) | |
| tree | 618c5f030f9a77d240dc59b132dd1e152baca116 /django/db/models/sql | |
| parent | aa8ee6a5731b37b73635e7605521fb1a54a5c10d (diff) | |
Fixed #24031 -- Added CASE expressions to the ORM.
Diffstat (limited to 'django/db/models/sql')
| -rw-r--r-- | django/db/models/sql/compiler.py | 4 | ||||
| -rw-r--r-- | django/db/models/sql/query.py | 26 | ||||
| -rw-r--r-- | django/db/models/sql/where.py | 25 |
3 files changed, 42 insertions, 13 deletions
diff --git a/django/db/models/sql/compiler.py b/django/db/models/sql/compiler.py index b091571835..ebda3be96f 100644 --- a/django/db/models/sql/compiler.py +++ b/django/db/models/sql/compiler.py @@ -998,7 +998,9 @@ class SQLUpdateCompiler(SQLCompiler): values, update_params = [], [] for field, model, val in self.query.values: if hasattr(val, 'resolve_expression'): - val = val.resolve_expression(self.query, allow_joins=False) + val = val.resolve_expression(self.query, allow_joins=False, for_save=True) + if val.contains_aggregate: + raise FieldError("Aggregate functions are not allowed in this query") elif hasattr(val, 'prepare_database_save'): if field.rel: val = val.prepare_database_save(field) diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py index 5d4538b2d9..c5e7eab28c 100644 --- a/django/db/models/sql/query.py +++ b/django/db/models/sql/query.py @@ -961,7 +961,7 @@ class Query(object): self.append_annotation_mask([alias]) self.annotations[alias] = annotation - def prepare_lookup_value(self, value, lookups, can_reuse): + def prepare_lookup_value(self, value, lookups, can_reuse, allow_joins=True): # Default lookup if none given is exact. used_joins = [] if len(lookups) == 0: @@ -980,7 +980,7 @@ class Query(object): value = value() elif hasattr(value, 'resolve_expression'): pre_joins = self.alias_refcount.copy() - value = value.resolve_expression(self, reuse=can_reuse) + value = value.resolve_expression(self, reuse=can_reuse, allow_joins=allow_joins) used_joins = [k for k, v in self.alias_refcount.items() if v > pre_joins.get(k, 0)] # Subqueries need to use a different set of aliases than the # outer query. Call bump_prefix to change aliases of the inner @@ -1095,7 +1095,7 @@ class Query(object): (name, lhs.output_field.__class__.__name__)) def build_filter(self, filter_expr, branch_negated=False, current_negated=False, - can_reuse=None, connector=AND): + can_reuse=None, connector=AND, allow_joins=True): """ Builds a WhereNode for a single filter clause, but doesn't add it to this Query. Query.add_q() will then add this filter to the where @@ -1125,10 +1125,12 @@ class Query(object): if not arg: raise FieldError("Cannot parse keyword query %r" % arg) lookups, parts, reffed_aggregate = self.solve_lookup_type(arg) + if not allow_joins and len(parts) > 1: + raise FieldError("Joined field references are not permitted in this query") # Work out the lookup type and remove it from the end of 'parts', # if necessary. - value, lookups, used_joins = self.prepare_lookup_value(value, lookups, can_reuse) + value, lookups, used_joins = self.prepare_lookup_value(value, lookups, can_reuse, allow_joins) clause = self.where_class() if reffed_aggregate: @@ -1225,11 +1227,11 @@ class Query(object): """ if not self._annotations: return False - if not isinstance(obj, Node): - return (refs_aggregate(obj[0].split(LOOKUP_SEP), self.annotations)[0] - or (hasattr(obj[1], 'refs_aggregate') - and obj[1].refs_aggregate(self.annotations)[0])) - return any(self.need_having(c) for c in obj.children) + if hasattr(obj, 'refs_aggregate'): + return obj.refs_aggregate(self.annotations)[0] + return (refs_aggregate(obj[0].split(LOOKUP_SEP), self.annotations)[0] + or (hasattr(obj[1], 'refs_aggregate') + and obj[1].refs_aggregate(self.annotations)[0])) def split_having_parts(self, q_object, negated=False): """ @@ -1287,7 +1289,7 @@ class Query(object): self.demote_joins(existing_inner) def _add_q(self, q_object, used_aliases, branch_negated=False, - current_negated=False): + current_negated=False, allow_joins=True): """ Adds a Q-object to the current filter. """ @@ -1301,12 +1303,12 @@ class Query(object): if isinstance(child, Node): child_clause, needed_inner = self._add_q( child, used_aliases, branch_negated, - current_negated) + current_negated, allow_joins) joinpromoter.add_votes(needed_inner) else: child_clause, needed_inner = self.build_filter( child, can_reuse=used_aliases, branch_negated=branch_negated, - current_negated=current_negated, connector=connector) + current_negated=current_negated, connector=connector, allow_joins=allow_joins) joinpromoter.add_votes(needed_inner) target_clause.add(child_clause, connector) needed_inner = joinpromoter.update_join_types(self) diff --git a/django/db/models/sql/where.py b/django/db/models/sql/where.py index 10445555e0..cbb709dfc8 100644 --- a/django/db/models/sql/where.py +++ b/django/db/models/sql/where.py @@ -11,6 +11,7 @@ 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.utils.functional import cached_property from django.utils.six.moves import range from django.utils import timezone from django.utils import tree @@ -309,6 +310,30 @@ class WhereNode(tree.Node): clone.children.append(child) return clone + def relabeled_clone(self, change_map): + clone = self.clone() + clone.relabel_aliases(change_map) + return clone + + @cached_property + def contains_aggregate(self): + def _contains_aggregate(obj): + if not isinstance(obj, tree.Node): + return getattr(obj.lhs, 'contains_aggregate', False) or getattr(obj.rhs, 'contains_aggregate', False) + return any(_contains_aggregate(c) for c in obj.children) + + return _contains_aggregate(self) + + def refs_field(self, aggregate_types, field_types): + def _refs_field(obj, aggregate_types, field_types): + if not isinstance(obj, tree.Node): + if hasattr(obj.rhs, 'refs_field'): + return obj.rhs.refs_field(aggregate_types, field_types) + return False + return any(_refs_field(c, aggregate_types, field_types) for c in obj.children) + + return _refs_field(self, aggregate_types, field_types) + class EmptyWhere(WhereNode): def add(self, data, connector): |
