summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoranabelensc <belensarabia@gmail.com>2015-12-14 19:13:21 +0000
committerTim Graham <timograham@gmail.com>2017-01-03 14:15:46 -0500
commit1c12df4aa6c599959d9eb6de2076bf8aa6e301d6 (patch)
treee1101dd29aa196f69ba2542b6a3ef72c75741446
parentf0ef0c49e9284f262fbc63e8a497699ca4a248fe (diff)
Fixed #25912 -- Added binary left/right shift operators to F expressions.
Thanks Mariusz Felisiak for review and MySQL advice.
-rw-r--r--AUTHORS1
-rw-r--r--django/db/backends/mysql/operations.py5
-rw-r--r--django/db/backends/oracle/operations.py7
-rw-r--r--django/db/models/expressions.py8
-rw-r--r--docs/releases/1.11.txt3
-rw-r--r--docs/topics/db/queries.txt8
-rw-r--r--tests/expressions/tests.py10
7 files changed, 37 insertions, 5 deletions
diff --git a/AUTHORS b/AUTHORS
index 9faa62271a..e81431be7d 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -42,6 +42,7 @@ answer newbie questions, and generally made Django that much better:
Amit Ramon <amit.ramon@gmail.com>
Amit Upadhyay <http://www.amitu.com/blog/>
A. Murat Eren <meren@pardus.org.tr>
+ Ana Belen Sarabia <belensarabia@gmail.com>
Ana Krivokapic <https://github.com/infraredgirl>
Andi Albrecht <albrecht.andi@gmail.com>
André Ericson <de.ericson@gmail.com>
diff --git a/django/db/backends/mysql/operations.py b/django/db/backends/mysql/operations.py
index a9c39b5363..43c39441c2 100644
--- a/django/db/backends/mysql/operations.py
+++ b/django/db/backends/mysql/operations.py
@@ -203,8 +203,11 @@ class DatabaseOperations(BaseDatabaseOperations):
return 'POW(%s)' % ','.join(sub_expressions)
# Convert the result to a signed integer since MySQL's binary operators
# return an unsigned integer.
- elif connector in ('&', '|'):
+ elif connector in ('&', '|', '<<'):
return 'CONVERT(%s, SIGNED)' % connector.join(sub_expressions)
+ elif connector == '>>':
+ lhs, rhs = sub_expressions
+ return 'FLOOR(%(lhs)s / POW(2, %(rhs)s))' % {'lhs': lhs, 'rhs': rhs}
return super(DatabaseOperations, self).combine_expression(connector, sub_expressions)
def get_db_converters(self, expression):
diff --git a/django/db/backends/oracle/operations.py b/django/db/backends/oracle/operations.py
index e283c574f2..74f05d5abe 100644
--- a/django/db/backends/oracle/operations.py
+++ b/django/db/backends/oracle/operations.py
@@ -436,14 +436,17 @@ WHEN (new.%(col_name)s IS NULL)
value.second, value.microsecond)
def combine_expression(self, connector, sub_expressions):
- "Oracle requires special cases for %% and & operators in query expressions"
+ lhs, rhs = sub_expressions
if connector == '%%':
return 'MOD(%s)' % ','.join(sub_expressions)
elif connector == '&':
return 'BITAND(%s)' % ','.join(sub_expressions)
elif connector == '|':
- lhs, rhs = sub_expressions
return 'BITAND(-%(lhs)s-1,%(rhs)s)+%(lhs)s' % {'lhs': lhs, 'rhs': rhs}
+ elif connector == '<<':
+ return '(%(lhs)s * POWER(2, %(rhs)s))' % {'lhs': lhs, 'rhs': rhs}
+ elif connector == '>>':
+ return 'FLOOR(%(lhs)s / POWER(2, %(rhs)s))' % {'lhs': lhs, 'rhs': rhs}
elif connector == '^':
return 'POWER(%s)' % ','.join(sub_expressions)
return super(DatabaseOperations, self).combine_expression(connector, sub_expressions)
diff --git a/django/db/models/expressions.py b/django/db/models/expressions.py
index 3add089624..1ff4cd7735 100644
--- a/django/db/models/expressions.py
+++ b/django/db/models/expressions.py
@@ -30,6 +30,8 @@ class Combinable(object):
# usage.
BITAND = '&'
BITOR = '|'
+ BITLEFTSHIFT = '<<'
+ BITRIGHTSHIFT = '>>'
def _combine(self, other, connector, reversed, node=None):
if not hasattr(other, 'resolve_expression'):
@@ -76,6 +78,12 @@ class Combinable(object):
def bitand(self, other):
return self._combine(other, self.BITAND, False)
+ def bitleftshift(self, other):
+ return self._combine(other, self.BITLEFTSHIFT, False)
+
+ def bitrightshift(self, other):
+ return self._combine(other, self.BITRIGHTSHIFT, False)
+
def __or__(self, other):
raise NotImplementedError(
"Use .bitand() and .bitor() for bitwise logical operations."
diff --git a/docs/releases/1.11.txt b/docs/releases/1.11.txt
index 9593a5c658..abdfb08a27 100644
--- a/docs/releases/1.11.txt
+++ b/docs/releases/1.11.txt
@@ -367,6 +367,9 @@ Models
:meth:`~django.db.models.Expression.desc` to control
the ordering of null values.
+* The new ``F`` expression ``bitleftshift()`` and ``bitrightshift()`` methods
+ allow :ref:`bitwise shift operations <using-f-expressions-in-filters>`.
+
Requests and Responses
~~~~~~~~~~~~~~~~~~~~~~
diff --git a/docs/topics/db/queries.txt b/docs/topics/db/queries.txt
index 0d69099fa7..f1540cbb96 100644
--- a/docs/topics/db/queries.txt
+++ b/docs/topics/db/queries.txt
@@ -652,11 +652,15 @@ that were modified more than 3 days after they were published::
>>> from datetime import timedelta
>>> Entry.objects.filter(mod_date__gt=F('pub_date') + timedelta(days=3))
-The ``F()`` objects support bitwise operations by ``.bitand()`` and
-``.bitor()``, for example::
+The ``F()`` objects support bitwise operations by ``.bitand()``, ``.bitor()``,
+``.bitrightshift()``, and ``.bitleftshift()``. For example::
>>> F('somefield').bitand(16)
+.. versionchanged:: 1.11
+
+ Support for ``.bitrightshift()`` and ``.bitleftshift()`` was added.
+
The ``pk`` lookup shortcut
--------------------------
diff --git a/tests/expressions/tests.py b/tests/expressions/tests.py
index ff9f7278c5..18d003d57d 100644
--- a/tests/expressions/tests.py
+++ b/tests/expressions/tests.py
@@ -745,6 +745,16 @@ class ExpressionOperatorTests(TestCase):
self.assertEqual(Number.objects.get(pk=self.n1.pk).integer, -64)
self.assertEqual(Number.objects.get(pk=self.n.pk).float, Approximate(15.500, places=3))
+ def test_lefthand_bitwise_left_shift_operator(self):
+ Number.objects.update(integer=F('integer').bitleftshift(2))
+ self.assertEqual(Number.objects.get(pk=self.n.pk).integer, 168)
+ self.assertEqual(Number.objects.get(pk=self.n1.pk).integer, -168)
+
+ def test_lefthand_bitwise_right_shift_operator(self):
+ Number.objects.update(integer=F('integer').bitrightshift(2))
+ self.assertEqual(Number.objects.get(pk=self.n.pk).integer, 10)
+ self.assertEqual(Number.objects.get(pk=self.n1.pk).integer, -11)
+
def test_lefthand_bitwise_or(self):
# LH Bitwise or on integers
Number.objects.update(integer=F('integer').bitor(48))