Ticket #16211: issue16211+14029-query-expression-extra-operators4.diff
File issue16211+14029-query-expression-extra-operators4.diff, 12.3 KB (added by , 12 years ago) |
---|
-
django/db/backends/__init__.py
diff --git a/django/db/backends/__init__.py b/django/db/backends/__init__.py index 9b0f495..8b50ce0 100644
a b class BaseDatabaseOperations(object): 912 912 can vary between backends (e.g., Oracle with %% and &) and between 913 913 subexpression types (e.g., date expressions) 914 914 """ 915 if connector == 'NOT': 916 assert len(sub_expressions) == 1 917 return 'NOT (%s)' % sub_expressions[0] 915 918 conn = ' %s ' % connector 916 919 return conn.join(sub_expressions) 917 920 -
django/db/models/expressions.py
diff --git a/django/db/models/expressions.py b/django/db/models/expressions.py index 639ef6e..972440b 100644
a b class ExpressionNode(tree.Node): 18 18 AND = '&' 19 19 OR = '|' 20 20 21 # Unary operator (needs special attention in combine_expression) 22 NOT = 'NOT' 23 24 # Comparison operators 25 EQ = '=' 26 GE = '>=' 27 GT = '>' 28 LE = '<=' 29 LT = '<' 30 NE = '<>' 31 21 32 def __init__(self, children=None, connector=None, negated=False): 22 33 if children is not None and len(children) > 1 and connector is None: 23 34 raise TypeError('You have to specify a connector.') … … class ExpressionNode(tree.Node): 93 104 def __ror__(self, other): 94 105 return self._combine(other, self.OR, True) 95 106 107 def __invert__(self): 108 obj = ExpressionNode([self], connector=self.NOT, negated=True) 109 return obj 110 111 def __eq__(self, other): 112 return self._combine(other, self.EQ, False) 113 114 def __ge__(self, other): 115 return self._combine(other, self.GE, False) 116 117 def __gt__(self, other): 118 return self._combine(other, self.GT, False) 119 120 def __le__(self, other): 121 return self._combine(other, self.LE, False) 122 123 def __lt__(self, other): 124 return self._combine(other, self.LT, False) 125 126 def __ne__(self, other): 127 return self._combine(other, self.NE, False) 128 129 def __bool__(self): 130 raise TypeError('Boolean operators should be avoided. Use bitwise operators.') 131 __nonzero__ = __bool__ 132 96 133 def prepare_database_save(self, unused): 97 134 return self 98 135 -
django/utils/tree.py
diff --git a/django/utils/tree.py b/django/utils/tree.py index 717181d..090e5c9 100644
a b class Node(object): 88 88 Otherwise, the whole tree is pushed down one level and a new root 89 89 connector is created, connecting the existing tree and the new node. 90 90 """ 91 if node in self.children and conn_type == self.connector: 92 return 91 # Using for loop with 'is' instead of 'if node in children' so node 92 # __eq__ method doesn't get called 93 for child in self.children: 94 if node is child and conn_type == self.connector: 95 return 93 96 if len(self.children) < 2: 94 97 self.connector = conn_type 95 98 if self.connector == conn_type: -
docs/releases/1.5.txt
diff --git a/docs/releases/1.5.txt b/docs/releases/1.5.txt index 528a44c..6d37620 100644
a b Django 1.5 also includes several smaller improvements worth noting: 129 129 configuration duplication. More information can be found in the 130 130 :func:`~django.contrib.auth.decorators.login_required` documentation. 131 131 132 * The :class:`~django.db.models.expressions.ExpressionNode` object now supports 133 comparison operations and inversion, expanding the types of expressions that 134 can be passed to the database. 135 132 136 Backwards incompatible changes in 1.5 133 137 ===================================== 134 138 -
docs/topics/db/queries.txt
diff --git a/docs/topics/db/queries.txt b/docs/topics/db/queries.txt index dd16065..9a2544b 100644
a b a join with an ``F()`` object, a ``FieldError`` will be raised:: 990 990 # THIS WILL RAISE A FieldError 991 991 >>> Entry.objects.update(headline=F('blog__name')) 992 992 993 .. versionadded:: 1.5 994 995 Django also supports the comparison operators ``==``, ``!=``, ``<=``, ``<``, 996 ``>``, ``>=`` and the bitwise negation operator ``~`` (boolean ``not`` operator 997 will raise ``TypeError``):: 998 999 >>> Entry.objects.update(is_heavily_quoted=~(F('n_pingbacks') < 100)) 1000 993 1001 .. _topics-db-queries-related: 994 1002 995 1003 Related objects -
tests/modeltests/expressions/models.py
diff --git a/tests/modeltests/expressions/models.py b/tests/modeltests/expressions/models.py index f592a0e..15f0d24 100644
a b class Company(models.Model): 27 27 Employee, 28 28 related_name='company_point_of_contact_set', 29 29 null=True) 30 is_large = models.BooleanField( 31 blank=True) 30 32 31 33 def __str__(self): 32 34 return self.name -
tests/modeltests/expressions/tests.py
diff --git a/tests/modeltests/expressions/tests.py b/tests/modeltests/expressions/tests.py index 99eb07e..66407c7 100644
a b from .models import Company, Employee 11 11 class ExpressionsTests(TestCase): 12 12 def test_filter(self): 13 13 Company.objects.create( 14 name="Example Inc.", num_employees=2300, num_chairs=5, 14 name="Example Inc.", num_employees=2300, num_chairs=5, is_large=False, 15 15 ceo=Employee.objects.create(firstname="Joe", lastname="Smith") 16 16 ) 17 17 Company.objects.create( 18 name="Foobar Ltd.", num_employees=3, num_chairs=4, 18 name="Foobar Ltd.", num_employees=3, num_chairs=4, is_large=False, 19 19 ceo=Employee.objects.create(firstname="Frank", lastname="Meyer") 20 20 ) 21 21 Company.objects.create( 22 name="Test GmbH", num_employees=32, num_chairs=1, 22 name="Test GmbH", num_employees=32, num_chairs=1, is_large=False, 23 23 ceo=Employee.objects.create(firstname="Max", lastname="Mustermann") 24 24 ) 25 25 26 26 company_query = Company.objects.values( 27 "name", "num_employees", "num_chairs" 27 "name", "num_employees", "num_chairs", "is_large" 28 28 ).order_by( 29 "name", "num_employees", "num_chairs" 29 "name", "num_employees", "num_chairs", "is_large" 30 30 ) 31 31 32 32 # We can filter for companies where the number of employees is greater … … class ExpressionsTests(TestCase): 37 37 "num_chairs": 5, 38 38 "name": "Example Inc.", 39 39 "num_employees": 2300, 40 "is_large": False 40 41 }, 41 42 { 42 43 "num_chairs": 1, 43 44 "name": "Test GmbH", 44 "num_employees": 32 45 "num_employees": 32, 46 "is_large": False 45 47 }, 46 48 ], 47 49 lambda o: o … … class ExpressionsTests(TestCase): 55 57 { 56 58 "num_chairs": 2300, 57 59 "name": "Example Inc.", 58 "num_employees": 2300 60 "num_employees": 2300, 61 "is_large": False 59 62 }, 60 63 { 61 64 "num_chairs": 3, 62 65 "name": "Foobar Ltd.", 63 "num_employees": 3 66 "num_employees": 3, 67 "is_large": False 64 68 }, 65 69 { 66 70 "num_chairs": 32, 67 71 "name": "Test GmbH", 68 "num_employees": 32 72 "num_employees": 32, 73 "is_large": False 69 74 } 70 75 ], 71 76 lambda o: o … … class ExpressionsTests(TestCase): 79 84 { 80 85 'num_chairs': 2302, 81 86 'name': 'Example Inc.', 82 'num_employees': 2300 87 'num_employees': 2300, 88 'is_large': False 83 89 }, 84 90 { 85 91 'num_chairs': 5, 86 92 'name': 'Foobar Ltd.', 87 'num_employees': 3 93 'num_employees': 3, 94 'is_large': False 88 95 }, 89 96 { 90 97 'num_chairs': 34, 91 98 'name': 'Test GmbH', 92 'num_employees': 32 99 'num_employees': 32, 100 'is_large': False 93 101 } 94 102 ], 95 103 lambda o: o, … … class ExpressionsTests(TestCase): 104 112 { 105 113 'num_chairs': 6900, 106 114 'name': 'Example Inc.', 107 'num_employees': 2300 115 'num_employees': 2300, 116 'is_large': False 108 117 }, 109 118 { 110 119 'num_chairs': 9, 111 120 'name': 'Foobar Ltd.', 112 'num_employees': 3 121 'num_employees': 3, 122 'is_large': False 113 123 }, 114 124 { 115 125 'num_chairs': 96, 116 126 'name': 'Test GmbH', 117 'num_employees': 32 127 'num_employees': 32, 128 'is_large': False 118 129 } 119 130 ], 120 131 lambda o: o, … … class ExpressionsTests(TestCase): 129 140 { 130 141 'num_chairs': 5294600, 131 142 'name': 'Example Inc.', 132 'num_employees': 2300 143 'num_employees': 2300, 144 'is_large': False 133 145 }, 134 146 { 135 147 'num_chairs': 15, 136 148 'name': 'Foobar Ltd.', 137 'num_employees': 3 149 'num_employees': 3, 150 'is_large': False 138 151 }, 139 152 { 140 153 'num_chairs': 1088, 141 154 'name': 'Test GmbH', 142 'num_employees': 32 155 'num_employees': 32, 156 'is_large': False 143 157 } 144 158 ], 145 159 lambda o: o, 146 160 ) 161 # The comparison operators and the bitwise unary not can be used 162 # to assign to boolean fields 163 for expression in ( 164 # Check boundaries 165 ~(F('num_employees') < 33), 166 ~(F('num_employees') <= 32), 167 (F('num_employees') > 2299), 168 (F('num_employees') >= 2300), 169 (F('num_employees') == 2300), 170 ((F('num_employees') + 1 != 4) & (32 != F('num_employees'))), 171 # Inverted argument order works too 172 (2299 < F('num_employees')), 173 (2300 <= F('num_employees')) 174 ): 175 # Test update by F-expression 176 company_query.update( 177 is_large=expression 178 ) 179 # Compare results 180 self.assertQuerysetEqual( 181 company_query, [ 182 { 183 'num_chairs': 5294600, 184 'name': u'Example Inc.', 185 'num_employees': 2300, 186 'is_large': True 187 }, 188 { 189 'num_chairs': 15, 190 'name': u'Foobar Ltd.', 191 'num_employees': 3, 192 'is_large': False 193 }, 194 { 195 'num_chairs': 1088, 196 'name': u'Test GmbH', 197 'num_employees': 32, 198 'is_large': False 199 } 200 ], 201 lambda o: o, 202 ) 203 # Reset values 204 company_query.update( 205 is_large=False 206 ) 207 208 # The python boolean operators should be avoided as they yield 209 # unexpected results 210 test_gmbh = Company.objects.get(name="Test GmbH") 211 def test(): 212 test_gmbh.is_large = not F('is_large') 213 self.assertRaises(TypeError, test) 214 def test(): 215 test_gmbh.is_large = F('is_large') and F('is_large') 216 self.assertRaises(TypeError, test) 217 def test(): 218 test_gmbh.is_large = F('is_large') or F('is_large') 219 self.assertRaises(TypeError, test) 147 220 148 221 # The relation of a foreign key can become copied over to an other 149 222 # foreign key.