Revert "Fixed #16211 -- Added comparison and negation ops to F() expressions"

This reverts commit 28abf5f0eb.

Conflicts:

	docs/releases/1.5.txt
This commit is contained in:
Anssi Kääriäinen 2012-10-03 18:21:39 +03:00
parent 7f4dbdc036
commit a8b1861fc4
7 changed files with 22 additions and 150 deletions

View File

@ -913,9 +913,6 @@ class BaseDatabaseOperations(object):
can vary between backends (e.g., Oracle with %% and &) and between can vary between backends (e.g., Oracle with %% and &) and between
subexpression types (e.g., date expressions) subexpression types (e.g., date expressions)
""" """
if connector == 'NOT':
assert len(sub_expressions) == 1
return 'NOT (%s)' % sub_expressions[0]
conn = ' %s ' % connector conn = ' %s ' % connector
return conn.join(sub_expressions) return conn.join(sub_expressions)

View File

@ -18,17 +18,6 @@ class ExpressionNode(tree.Node):
AND = '&' AND = '&'
OR = '|' 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): def __init__(self, children=None, connector=None, negated=False):
if children is not None and len(children) > 1 and connector is None: if children is not None and len(children) > 1 and connector is None:
raise TypeError('You have to specify a connector.') raise TypeError('You have to specify a connector.')
@ -104,32 +93,6 @@ class ExpressionNode(tree.Node):
def __ror__(self, other): def __ror__(self, other):
return self._combine(other, self.OR, True) 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): def prepare_database_save(self, unused):
return self return self

View File

@ -88,11 +88,7 @@ class Node(object):
Otherwise, the whole tree is pushed down one level and a new root Otherwise, the whole tree is pushed down one level and a new root
connector is created, connecting the existing tree and the new node. connector is created, connecting the existing tree and the new node.
""" """
# Using for loop with 'is' instead of 'if node in children' so node if node in self.children and conn_type == self.connector:
# __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 return
if len(self.children) < 2: if len(self.children) < 2:
self.connector = conn_type self.connector = conn_type

View File

@ -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 :setting:`DEBUG` is `True` are sent to the console (unless you redefine the
logger in your :setting:`LOGGING` setting). logger in your :setting:`LOGGING` setting).
* :ref:`F() expressions <query-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 * When using :class:`~django.template.RequestContext`, it is now possible to
look up permissions by using ``{% if 'someapp.someperm' in perms %}`` look up permissions by using ``{% if 'someapp.someperm' in perms %}``
in templates. in templates.

View File

@ -640,15 +640,6 @@ that were modified more than 3 days after they were published::
>>> from datetime import timedelta >>> from datetime import timedelta
>>> Entry.objects.filter(mod_date__gt=F('pub_date') + timedelta(days=3)) >>> 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 The pk lookup shortcut
---------------------- ----------------------

View File

@ -27,8 +27,6 @@ class Company(models.Model):
Employee, Employee,
related_name='company_point_of_contact_set', related_name='company_point_of_contact_set',
null=True) null=True)
is_large = models.BooleanField(
blank=True)
def __str__(self): def __str__(self):
return self.name return self.name

View File

@ -11,22 +11,22 @@ from .models import Company, Employee
class ExpressionsTests(TestCase): class ExpressionsTests(TestCase):
def test_filter(self): def test_filter(self):
Company.objects.create( 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") ceo=Employee.objects.create(firstname="Joe", lastname="Smith")
) )
Company.objects.create( 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") ceo=Employee.objects.create(firstname="Frank", lastname="Meyer")
) )
Company.objects.create( 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") ceo=Employee.objects.create(firstname="Max", lastname="Mustermann")
) )
company_query = Company.objects.values( company_query = Company.objects.values(
"name", "num_employees", "num_chairs", "is_large" "name", "num_employees", "num_chairs"
).order_by( ).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 # We can filter for companies where the number of employees is greater
@ -37,13 +37,11 @@ class ExpressionsTests(TestCase):
"num_chairs": 5, "num_chairs": 5,
"name": "Example Inc.", "name": "Example Inc.",
"num_employees": 2300, "num_employees": 2300,
"is_large": False
}, },
{ {
"num_chairs": 1, "num_chairs": 1,
"name": "Test GmbH", "name": "Test GmbH",
"num_employees": 32, "num_employees": 32
"is_large": False
}, },
], ],
lambda o: o lambda o: o
@ -57,20 +55,17 @@ class ExpressionsTests(TestCase):
{ {
"num_chairs": 2300, "num_chairs": 2300,
"name": "Example Inc.", "name": "Example Inc.",
"num_employees": 2300, "num_employees": 2300
"is_large": False
}, },
{ {
"num_chairs": 3, "num_chairs": 3,
"name": "Foobar Ltd.", "name": "Foobar Ltd.",
"num_employees": 3, "num_employees": 3
"is_large": False
}, },
{ {
"num_chairs": 32, "num_chairs": 32,
"name": "Test GmbH", "name": "Test GmbH",
"num_employees": 32, "num_employees": 32
"is_large": False
} }
], ],
lambda o: o lambda o: o
@ -84,20 +79,17 @@ class ExpressionsTests(TestCase):
{ {
'num_chairs': 2302, 'num_chairs': 2302,
'name': 'Example Inc.', 'name': 'Example Inc.',
'num_employees': 2300, 'num_employees': 2300
'is_large': False
}, },
{ {
'num_chairs': 5, 'num_chairs': 5,
'name': 'Foobar Ltd.', 'name': 'Foobar Ltd.',
'num_employees': 3, 'num_employees': 3
'is_large': False
}, },
{ {
'num_chairs': 34, 'num_chairs': 34,
'name': 'Test GmbH', 'name': 'Test GmbH',
'num_employees': 32, 'num_employees': 32
'is_large': False
} }
], ],
lambda o: o, lambda o: o,
@ -112,20 +104,17 @@ class ExpressionsTests(TestCase):
{ {
'num_chairs': 6900, 'num_chairs': 6900,
'name': 'Example Inc.', 'name': 'Example Inc.',
'num_employees': 2300, 'num_employees': 2300
'is_large': False
}, },
{ {
'num_chairs': 9, 'num_chairs': 9,
'name': 'Foobar Ltd.', 'name': 'Foobar Ltd.',
'num_employees': 3, 'num_employees': 3
'is_large': False
}, },
{ {
'num_chairs': 96, 'num_chairs': 96,
'name': 'Test GmbH', 'name': 'Test GmbH',
'num_employees': 32, 'num_employees': 32
'is_large': False
} }
], ],
lambda o: o, lambda o: o,
@ -140,80 +129,21 @@ class ExpressionsTests(TestCase):
{ {
'num_chairs': 5294600, 'num_chairs': 5294600,
'name': 'Example Inc.', 'name': 'Example Inc.',
'num_employees': 2300, 'num_employees': 2300
'is_large': False
}, },
{ {
'num_chairs': 15, 'num_chairs': 15,
'name': 'Foobar Ltd.', 'name': 'Foobar Ltd.',
'num_employees': 3, 'num_employees': 3
'is_large': False
}, },
{ {
'num_chairs': 1088, 'num_chairs': 1088,
'name': 'Test GmbH', 'name': 'Test GmbH',
'num_employees': 32, 'num_employees': 32
'is_large': False
} }
], ],
lambda o: o, 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 # The relation of a foreign key can become copied over to an other
# foreign key. # foreign key.
@ -272,8 +202,9 @@ class ExpressionsTests(TestCase):
test_gmbh.point_of_contact = None test_gmbh.point_of_contact = None
test_gmbh.save() test_gmbh.save()
self.assertTrue(test_gmbh.point_of_contact is None) self.assertTrue(test_gmbh.point_of_contact is None)
with self.assertRaises(ValueError): def test():
test_gmbh.point_of_contact = F("ceo") test_gmbh.point_of_contact = F("ceo")
self.assertRaises(ValueError, test)
test_gmbh.point_of_contact = test_gmbh.ceo test_gmbh.point_of_contact = test_gmbh.ceo
test_gmbh.save() test_gmbh.save()