summaryrefslogtreecommitdiff
path: root/django/db/models/sql
diff options
context:
space:
mode:
authorMichaƂ Modzelewski <michal.modzelewski@gmail.com>2015-01-02 02:39:31 +0100
committerTim Graham <timograham@gmail.com>2015-01-12 18:15:34 -0500
commit65246de7b1d70d25831ab394c4f4a75813f629fe (patch)
tree618c5f030f9a77d240dc59b132dd1e152baca116 /django/db/models/sql
parentaa8ee6a5731b37b73635e7605521fb1a54a5c10d (diff)
Fixed #24031 -- Added CASE expressions to the ORM.
Diffstat (limited to 'django/db/models/sql')
-rw-r--r--django/db/models/sql/compiler.py4
-rw-r--r--django/db/models/sql/query.py26
-rw-r--r--django/db/models/sql/where.py25
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):