diff --git a/django/db/backends/__init__.py b/django/db/backends/__init__.py
index 99f8f27..6b027de 100644
a
|
b
|
class BaseDatabaseOperations(object):
|
408 | 408 | """ |
409 | 409 | pass |
410 | 410 | |
| 411 | def combine_expression(self, connector, sub_expressions): |
| 412 | """Combine a list of subexpressions into a single expression, using |
| 413 | the provided connecting operator. This is required because operators |
| 414 | can vary between backends (e.g., Oracle with %% and &) and between |
| 415 | subexpression types (e.g., date expressions) |
| 416 | """ |
| 417 | conn = ' %s ' % connector |
| 418 | return conn.join(sub_expressions) |
| 419 | |
411 | 420 | class BaseDatabaseIntrospection(object): |
412 | 421 | """ |
413 | 422 | This class encapsulates all backend-specific introspection utilities |
diff --git a/django/db/backends/oracle/base.py b/django/db/backends/oracle/base.py
index d6bd3ea..02c25ec 100644
a
|
b
|
WHEN (new.%(col_name)s IS NULL)
|
221 | 221 | second = '%s-12-31' |
222 | 222 | return [first % value, second % value] |
223 | 223 | |
| 224 | def combine_expression(self, connector, sub_expressions): |
| 225 | "Oracle requires special cases for %% and & operators in query expressions" |
| 226 | if connector == '%%': |
| 227 | return 'MOD(%s)' % ','.join(sub_expressions) |
| 228 | elif: |
| 229 | return 'BITAND(%s)' % ','.join(sub_expressions) |
| 230 | return super(DatabaseOperations, self).expression(connector, sub_expressions, parameters) |
224 | 231 | |
225 | 232 | class DatabaseWrapper(BaseDatabaseWrapper): |
226 | 233 | |
diff --git a/django/db/models/sql/expressions.py b/django/db/models/sql/expressions.py
index ef9fcb0..f011db2 100644
a
|
b
|
class SQLEvaluator(object):
|
74 | 74 | if sql: |
75 | 75 | expressions.append(format % sql) |
76 | 76 | expression_params.extend(params) |
77 | | conn = ' %s ' % node.connector |
78 | 77 | |
79 | | return conn.join(expressions), expression_params |
| 78 | return connection.ops.combine_expression(node.connector, expressions), expression_params |
80 | 79 | |
81 | 80 | def evaluate_leaf(self, node, qn): |
82 | 81 | if not qn: |
diff --git a/tests/modeltests/expressions/models.py b/tests/modeltests/expressions/models.py
index 4043f5e..cccb05d 100644
a
|
b
|
__test__ = {'API_TESTS': """
|
37 | 37 | >>> Company(name='Test GmbH', num_employees=32, num_chairs=1, |
38 | 38 | ... ceo=Employee.objects.create(firstname='Max', lastname='Mustermann')).save() |
39 | 39 | |
| 40 | >>> company_query = Company.objects.values('name','num_employees','num_chairs').order_by('name','num_employees','num_chairs') |
| 41 | |
40 | 42 | # We can filter for companies where the number of employees is greater than the |
41 | 43 | # number of chairs. |
| 44 | >>> company_query.filter(num_employees__gt=F('num_chairs')) |
| 45 | [{'num_chairs': 5, 'name': u'Example Inc.', 'num_employees': 2300}, {'num_chairs': 1, 'name': u'Test GmbH', 'num_employees': 32}] |
42 | 46 | |
43 | | >>> Company.objects.filter(num_employees__gt=F('num_chairs')) |
44 | | [<Company: Example Inc.>, <Company: Test GmbH>] |
| 47 | # We can set one field to have the value of another field |
| 48 | # Make sure we have enough chairs |
| 49 | >>> company_query.update(num_chairs=F('num_employees')) |
| 50 | 3 |
| 51 | >>> company_query |
| 52 | [{'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}] |
45 | 53 | |
46 | | # The relation of a foreign key can become copied over to an other foreign key. |
| 54 | # We can perform arithmetic operations in expressions |
| 55 | # Make sure we have 2 spare chairs |
| 56 | >>> company_query.update(num_chairs=F('num_employees')+2) |
| 57 | 3 |
| 58 | >>> company_query |
| 59 | [{'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}] |
47 | 60 | |
| 61 | # The relation of a foreign key can become copied over to an other foreign key. |
48 | 62 | >>> Company.objects.update(point_of_contact=F('ceo')) |
49 | 63 | 3 |
50 | 64 | |
diff --git a/tests/regressiontests/expressions_regress/models.py b/tests/regressiontests/expressions_regress/models.py
index dfd0df0..ae75cdb 100644
a
|
b
|
|
1 | 1 | """ |
2 | 2 | Spanning tests for all the operations that F() expressions can perform. |
3 | 3 | """ |
4 | | |
| 4 | from django.conf import settings |
5 | 5 | from django.db import models |
6 | 6 | |
7 | 7 | # |
… |
… |
Complex expressions of different connection types are possible.
|
123 | 123 | >>> _ = Number.objects.filter(pk=n.pk).update(integer=15 & F('integer')) |
124 | 124 | >>> Number.objects.get(pk=n.pk) # RH Bitwise ands on integers |
125 | 125 | <Number: 10, 15.500> |
| 126 | """} |
| 127 | |
| 128 | # Oracle doesn't support the Bitwise OR operator. |
| 129 | if settings.DATABASE_ENGINE != 'oracle': |
| 130 | __test__['API_TESTS'] += """ |
126 | 131 | |
127 | 132 | >>> _ = Number.objects.filter(pk=n.pk).update(integer=42, float=15.5) |
128 | 133 | >>> _ = Number.objects.filter(pk=n.pk).update(integer=15 | F('integer')) |
129 | 134 | >>> Number.objects.get(pk=n.pk) # RH Bitwise or on integers |
130 | 135 | <Number: 47, 15.500> |
131 | 136 | |
132 | | |
133 | | """} |
| 137 | """ |