From a8b1861fc4d0a48b4879af803bba094eef145017 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anssi=20K=C3=A4=C3=A4ri=C3=A4inen?= Date: Wed, 3 Oct 2012 18:21:39 +0300 Subject: [PATCH] Revert "Fixed #16211 -- Added comparison and negation ops to F() expressions" This reverts commit 28abf5f0ebc9d380f25dd278d7ef4642c4504545. Conflicts: docs/releases/1.5.txt --- django/db/backends/__init__.py | 3 - django/db/models/expressions.py | 37 --------- django/utils/tree.py | 8 +- docs/releases/1.5.txt | 4 - docs/topics/db/queries.txt | 9 -- tests/modeltests/expressions/models.py | 2 - tests/modeltests/expressions/tests.py | 109 +++++-------------------- 7 files changed, 22 insertions(+), 150 deletions(-) diff --git a/django/db/backends/__init__.py b/django/db/backends/__init__.py index 4edde04f42..02d2a16a46 100644 --- a/django/db/backends/__init__.py +++ b/django/db/backends/__init__.py @@ -913,9 +913,6 @@ class BaseDatabaseOperations(object): can vary between backends (e.g., Oracle with %% and &) and between subexpression types (e.g., date expressions) """ - if connector == 'NOT': - assert len(sub_expressions) == 1 - return 'NOT (%s)' % sub_expressions[0] conn = ' %s ' % connector return conn.join(sub_expressions) diff --git a/django/db/models/expressions.py b/django/db/models/expressions.py index 972440b858..639ef6ee10 100644 --- a/django/db/models/expressions.py +++ b/django/db/models/expressions.py @@ -18,17 +18,6 @@ class ExpressionNode(tree.Node): AND = '&' OR = '|' - # Unary operator (needs special attention in combine_expression) - NOT = 'NOT' - - # Comparison operators - EQ = '=' - GE = '>=' - GT = '>' - LE = '<=' - LT = '<' - NE = '<>' - def __init__(self, children=None, connector=None, negated=False): if children is not None and len(children) > 1 and connector is None: raise TypeError('You have to specify a connector.') @@ -104,32 +93,6 @@ class ExpressionNode(tree.Node): def __ror__(self, other): return self._combine(other, self.OR, True) - def __invert__(self): - obj = ExpressionNode([self], connector=self.NOT, negated=True) - return obj - - def __eq__(self, other): - return self._combine(other, self.EQ, False) - - def __ge__(self, other): - return self._combine(other, self.GE, False) - - def __gt__(self, other): - return self._combine(other, self.GT, False) - - def __le__(self, other): - return self._combine(other, self.LE, False) - - def __lt__(self, other): - return self._combine(other, self.LT, False) - - def __ne__(self, other): - return self._combine(other, self.NE, False) - - def __bool__(self): - raise TypeError('Boolean operators should be avoided. Use bitwise operators.') - __nonzero__ = __bool__ - def prepare_database_save(self, unused): return self diff --git a/django/utils/tree.py b/django/utils/tree.py index 6229493544..717181d2b9 100644 --- a/django/utils/tree.py +++ b/django/utils/tree.py @@ -88,12 +88,8 @@ class Node(object): Otherwise, the whole tree is pushed down one level and a new root connector is created, connecting the existing tree and the new node. """ - # Using for loop with 'is' instead of 'if node in children' so node - # __eq__ method doesn't get called. The __eq__ method can be overriden - # by subtypes, for example the F-expression. - for child in self.children: - if node is child and conn_type == self.connector: - return + if node in self.children and conn_type == self.connector: + return if len(self.children) < 2: self.connector = conn_type if self.connector == conn_type: diff --git a/docs/releases/1.5.txt b/docs/releases/1.5.txt index e99b2fd578..78ba77308b 100644 --- a/docs/releases/1.5.txt +++ b/docs/releases/1.5.txt @@ -177,10 +177,6 @@ Django 1.5 also includes several smaller improvements worth noting: :setting:`DEBUG` is `True` are sent to the console (unless you redefine the logger in your :setting:`LOGGING` setting). -* :ref:`F() expressions ` now support comparison operations - and inversion, expanding the types of expressions that can be passed to the - database. - * When using :class:`~django.template.RequestContext`, it is now possible to look up permissions by using ``{% if 'someapp.someperm' in perms %}`` in templates. diff --git a/docs/topics/db/queries.txt b/docs/topics/db/queries.txt index 54f069248a..fa98c91739 100644 --- a/docs/topics/db/queries.txt +++ b/docs/topics/db/queries.txt @@ -640,15 +640,6 @@ 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)) -.. versionadded:: 1.5 - Comparisons and negation operators for ``F()`` expressions - -Django also supports the comparison operators ``==``, ``!=``, ``<=``, ``<``, -``>``, ``>=`` and the bitwise negation operator ``~`` (boolean ``not`` operator -will raise ``TypeError``):: - - >>> Entry.objects.filter(is_heavily_quoted=~(F('n_pingbacks') < 100)) - The pk lookup shortcut ---------------------- diff --git a/tests/modeltests/expressions/models.py b/tests/modeltests/expressions/models.py index 15f0d24541..f592a0eb13 100644 --- a/tests/modeltests/expressions/models.py +++ b/tests/modeltests/expressions/models.py @@ -27,8 +27,6 @@ class Company(models.Model): Employee, related_name='company_point_of_contact_set', null=True) - is_large = models.BooleanField( - blank=True) def __str__(self): return self.name diff --git a/tests/modeltests/expressions/tests.py b/tests/modeltests/expressions/tests.py index 14419ec55b..99eb07e370 100644 --- a/tests/modeltests/expressions/tests.py +++ b/tests/modeltests/expressions/tests.py @@ -11,22 +11,22 @@ from .models import Company, Employee class ExpressionsTests(TestCase): def test_filter(self): Company.objects.create( - name="Example Inc.", num_employees=2300, num_chairs=5, is_large=False, + name="Example Inc.", num_employees=2300, num_chairs=5, ceo=Employee.objects.create(firstname="Joe", lastname="Smith") ) Company.objects.create( - name="Foobar Ltd.", num_employees=3, num_chairs=4, is_large=False, + name="Foobar Ltd.", num_employees=3, num_chairs=4, ceo=Employee.objects.create(firstname="Frank", lastname="Meyer") ) Company.objects.create( - name="Test GmbH", num_employees=32, num_chairs=1, is_large=False, + name="Test GmbH", num_employees=32, num_chairs=1, ceo=Employee.objects.create(firstname="Max", lastname="Mustermann") ) company_query = Company.objects.values( - "name", "num_employees", "num_chairs", "is_large" + "name", "num_employees", "num_chairs" ).order_by( - "name", "num_employees", "num_chairs", "is_large" + "name", "num_employees", "num_chairs" ) # We can filter for companies where the number of employees is greater @@ -37,13 +37,11 @@ class ExpressionsTests(TestCase): "num_chairs": 5, "name": "Example Inc.", "num_employees": 2300, - "is_large": False }, { "num_chairs": 1, "name": "Test GmbH", - "num_employees": 32, - "is_large": False + "num_employees": 32 }, ], lambda o: o @@ -57,20 +55,17 @@ class ExpressionsTests(TestCase): { "num_chairs": 2300, "name": "Example Inc.", - "num_employees": 2300, - "is_large": False + "num_employees": 2300 }, { "num_chairs": 3, "name": "Foobar Ltd.", - "num_employees": 3, - "is_large": False + "num_employees": 3 }, { "num_chairs": 32, "name": "Test GmbH", - "num_employees": 32, - "is_large": False + "num_employees": 32 } ], lambda o: o @@ -84,20 +79,17 @@ class ExpressionsTests(TestCase): { 'num_chairs': 2302, 'name': 'Example Inc.', - 'num_employees': 2300, - 'is_large': False + 'num_employees': 2300 }, { 'num_chairs': 5, 'name': 'Foobar Ltd.', - 'num_employees': 3, - 'is_large': False + 'num_employees': 3 }, { 'num_chairs': 34, 'name': 'Test GmbH', - 'num_employees': 32, - 'is_large': False + 'num_employees': 32 } ], lambda o: o, @@ -112,20 +104,17 @@ class ExpressionsTests(TestCase): { 'num_chairs': 6900, 'name': 'Example Inc.', - 'num_employees': 2300, - 'is_large': False + 'num_employees': 2300 }, { 'num_chairs': 9, 'name': 'Foobar Ltd.', - 'num_employees': 3, - 'is_large': False + 'num_employees': 3 }, { 'num_chairs': 96, 'name': 'Test GmbH', - 'num_employees': 32, - 'is_large': False + 'num_employees': 32 } ], lambda o: o, @@ -140,80 +129,21 @@ class ExpressionsTests(TestCase): { 'num_chairs': 5294600, 'name': 'Example Inc.', - 'num_employees': 2300, - 'is_large': False + 'num_employees': 2300 }, { 'num_chairs': 15, 'name': 'Foobar Ltd.', - 'num_employees': 3, - 'is_large': False + 'num_employees': 3 }, { 'num_chairs': 1088, 'name': 'Test GmbH', - 'num_employees': 32, - 'is_large': False + 'num_employees': 32 } ], lambda o: o, ) - # The comparison operators and the bitwise unary not can be used - # to assign to boolean fields - for expression in ( - # Check boundaries - ~(F('num_employees') < 33), - ~(F('num_employees') <= 32), - (F('num_employees') > 2299), - (F('num_employees') >= 2300), - (F('num_employees') == 2300), - ((F('num_employees') + 1 != 4) & (32 != F('num_employees'))), - # Inverted argument order works too - (2299 < F('num_employees')), - (2300 <= F('num_employees')) - ): - # Test update by F-expression - company_query.update( - is_large=expression - ) - # Compare results - self.assertQuerysetEqual( - company_query, [ - { - 'num_chairs': 5294600, - 'name': 'Example Inc.', - 'num_employees': 2300, - 'is_large': True - }, - { - 'num_chairs': 15, - 'name': 'Foobar Ltd.', - 'num_employees': 3, - 'is_large': False - }, - { - 'num_chairs': 1088, - 'name': 'Test GmbH', - 'num_employees': 32, - 'is_large': False - } - ], - lambda o: o, - ) - # Reset values - company_query.update( - is_large=False - ) - - # The python boolean operators should be avoided as they yield - # unexpected results - test_gmbh = Company.objects.get(name="Test GmbH") - with self.assertRaises(TypeError): - test_gmbh.is_large = not F('is_large') - with self.assertRaises(TypeError): - test_gmbh.is_large = F('is_large') and F('is_large') - with self.assertRaises(TypeError): - test_gmbh.is_large = F('is_large') or F('is_large') # The relation of a foreign key can become copied over to an other # foreign key. @@ -272,8 +202,9 @@ class ExpressionsTests(TestCase): test_gmbh.point_of_contact = None test_gmbh.save() self.assertTrue(test_gmbh.point_of_contact is None) - with self.assertRaises(ValueError): + def test(): test_gmbh.point_of_contact = F("ceo") + self.assertRaises(ValueError, test) test_gmbh.point_of_contact = test_gmbh.ceo test_gmbh.save()