Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Fixed #10161 -- Modified evaluation of query expressions to allow for…

… operators that take the form of functions. This is mostly for the benefit of Oracle, but it should prove useful later on. Thanks to Ian for the report and feedback on the fix.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@9898 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 7d03ca9e86e366c2c369101621d011eb6ee8b2c2 1 parent 28605a9
@freakboy3742 freakboy3742 authored
View
9 django/db/backends/__init__.py
@@ -408,6 +408,15 @@ def check_aggregate_support(self, aggregate_func):
"""
pass
+ def combine_expression(self, connector, sub_expressions):
+ """Combine a list of subexpressions into a single expression, using
+ the provided connecting operator. This is required because operators
+ can vary between backends (e.g., Oracle with %% and &) and between
+ subexpression types (e.g., date expressions)
+ """
+ conn = ' %s ' % connector
+ return conn.join(sub_expressions)
+
class BaseDatabaseIntrospection(object):
"""
This class encapsulates all backend-specific introspection utilities
View
10 django/db/backends/oracle/base.py
@@ -221,6 +221,16 @@ def year_lookup_bounds_for_date_field(self, value):
second = '%s-12-31'
return [first % value, second % value]
+ def combine_expression(self, connector, sub_expressions):
+ "Oracle requires special cases for %% and & operators in query expressions"
+ if connector == '%%':
+ return 'MOD(%s)' % ','.join(sub_expressions)
+ elif connector == '&':
+ return 'BITAND(%s)' % ','.join(sub_expressions)
+ elif connector == '|':
+ raise NotImplementedError("Bit-wise or is not supported in Oracle.")
+ return super(DatabaseOperations, self).combine_expression(connector, sub_expressions)
+
class DatabaseWrapper(BaseDatabaseWrapper):
View
3  django/db/models/sql/expressions.py
@@ -74,9 +74,8 @@ def evaluate_node(self, node, qn):
if sql:
expressions.append(format % sql)
expression_params.extend(params)
- conn = ' %s ' % node.connector
- return conn.join(expressions), expression_params
+ return connection.ops.combine_expression(node.connector, expressions), expression_params
def evaluate_leaf(self, node, qn):
if not qn:
View
18 tests/modeltests/expressions/models.py
@@ -37,14 +37,26 @@ def __unicode__(self):
>>> Company(name='Test GmbH', num_employees=32, num_chairs=1,
... ceo=Employee.objects.create(firstname='Max', lastname='Mustermann')).save()
+>>> company_query = Company.objects.values('name','num_employees','num_chairs').order_by('name','num_employees','num_chairs')
+
# We can filter for companies where the number of employees is greater than the
# number of chairs.
+>>> company_query.filter(num_employees__gt=F('num_chairs'))
+[{'num_chairs': 5, 'name': u'Example Inc.', 'num_employees': 2300}, {'num_chairs': 1, 'name': u'Test GmbH', 'num_employees': 32}]
->>> Company.objects.filter(num_employees__gt=F('num_chairs'))
-[<Company: Example Inc.>, <Company: Test GmbH>]
+# We can set one field to have the value of another field
+# Make sure we have enough chairs
+>>> _ = company_query.update(num_chairs=F('num_employees'))
+>>> company_query
+[{'num_chairs': 2300, 'name': u'Example Inc.', 'num_employees': 2300}, {'num_chairs': 3, 'name': u'Foobar Ltd.', 'num_employees': 3}, {'num_chairs': 32, 'name': u'Test GmbH', 'num_employees': 32}]
-# The relation of a foreign key can become copied over to an other foreign key.
+# We can perform arithmetic operations in expressions
+# Make sure we have 2 spare chairs
+>>> _ =company_query.update(num_chairs=F('num_employees')+2)
+>>> company_query
+[{'num_chairs': 2302, 'name': u'Example Inc.', 'num_employees': 2300}, {'num_chairs': 5, 'name': u'Foobar Ltd.', 'num_employees': 3}, {'num_chairs': 34, 'name': u'Test GmbH', 'num_employees': 32}]
+# The relation of a foreign key can become copied over to an other foreign key.
>>> Company.objects.update(point_of_contact=F('ceo'))
3
View
20 tests/regressiontests/expressions_regress/models.py
@@ -1,7 +1,7 @@
"""
Spanning tests for all the operations that F() expressions can perform.
"""
-
+from django.conf import settings
from django.db import models
#
@@ -87,11 +87,6 @@ def __unicode__(self):
>>> Number.objects.get(pk=n.pk) # LH Bitwise ands on integers
<Number: 40, 15.500>
->>> _ = Number.objects.filter(pk=n.pk).update(integer=42, float=15.5)
->>> _ = Number.objects.filter(pk=n.pk).update(integer=F('integer') | 48)
->>> Number.objects.get(pk=n.pk) # LH Bitwise or on integers
-<Number: 58, 15.500>
-
# Right hand operators
>>> _ = Number.objects.filter(pk=n.pk).update(integer=42, float=15.5)
@@ -123,11 +118,20 @@ def __unicode__(self):
>>> _ = Number.objects.filter(pk=n.pk).update(integer=15 & F('integer'))
>>> Number.objects.get(pk=n.pk) # RH Bitwise ands on integers
<Number: 10, 15.500>
+"""}
+
+# Oracle doesn't support the Bitwise OR operator.
+if settings.DATABASE_ENGINE != 'oracle':
+ __test__['API_TESTS'] += """
+
+>>> _ = Number.objects.filter(pk=n.pk).update(integer=42, float=15.5)
+>>> _ = Number.objects.filter(pk=n.pk).update(integer=F('integer') | 48)
+>>> Number.objects.get(pk=n.pk) # LH Bitwise or on integers
+<Number: 58, 15.500>
>>> _ = Number.objects.filter(pk=n.pk).update(integer=42, float=15.5)
>>> _ = Number.objects.filter(pk=n.pk).update(integer=15 | F('integer'))
>>> Number.objects.get(pk=n.pk) # RH Bitwise or on integers
<Number: 47, 15.500>
-
-"""}
+"""
Please sign in to comment.
Something went wrong with that request. Please try again.