|
1 from django.core.exceptions import FieldError |
|
2 from django.db.models import F |
|
3 from django.test import TestCase |
|
4 |
|
5 from models import Company, Employee |
|
6 |
|
7 |
|
8 class ExpressionsTests(TestCase): |
|
9 def test_filter(self): |
|
10 Company.objects.create( |
|
11 name="Example Inc.", num_employees=2300, num_chairs=5, |
|
12 ceo=Employee.objects.create(firstname="Joe", lastname="Smith") |
|
13 ) |
|
14 Company.objects.create( |
|
15 name="Foobar Ltd.", num_employees=3, num_chairs=4, |
|
16 ceo=Employee.objects.create(firstname="Frank", lastname="Meyer") |
|
17 ) |
|
18 Company.objects.create( |
|
19 name="Test GmbH", num_employees=32, num_chairs=1, |
|
20 ceo=Employee.objects.create(firstname="Max", lastname="Mustermann") |
|
21 ) |
|
22 |
|
23 company_query = Company.objects.values( |
|
24 "name", "num_employees", "num_chairs" |
|
25 ).order_by( |
|
26 "name", "num_employees", "num_chairs" |
|
27 ) |
|
28 |
|
29 # We can filter for companies where the number of employees is greater |
|
30 # than the number of chairs. |
|
31 self.assertQuerysetEqual( |
|
32 company_query.filter(num_employees__gt=F("num_chairs")), [ |
|
33 { |
|
34 "num_chairs": 5, |
|
35 "name": "Example Inc.", |
|
36 "num_employees": 2300, |
|
37 }, |
|
38 { |
|
39 "num_chairs": 1, |
|
40 "name": "Test GmbH", |
|
41 "num_employees": 32 |
|
42 }, |
|
43 ], |
|
44 lambda o: o |
|
45 ) |
|
46 |
|
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 self.assertQuerysetEqual( |
|
51 company_query, [ |
|
52 { |
|
53 "num_chairs": 2300, |
|
54 "name": "Example Inc.", |
|
55 "num_employees": 2300 |
|
56 }, |
|
57 { |
|
58 "num_chairs": 3, |
|
59 "name": "Foobar Ltd.", |
|
60 "num_employees": 3 |
|
61 }, |
|
62 { |
|
63 "num_chairs": 32, |
|
64 "name": "Test GmbH", |
|
65 "num_employees": 32 |
|
66 } |
|
67 ], |
|
68 lambda o: o |
|
69 ) |
|
70 |
|
71 # We can perform arithmetic operations in expressions |
|
72 # Make sure we have 2 spare chairs |
|
73 company_query.update(num_chairs=F("num_employees")+2) |
|
74 self.assertQuerysetEqual( |
|
75 company_query, [ |
|
76 { |
|
77 'num_chairs': 2302, |
|
78 'name': u'Example Inc.', |
|
79 'num_employees': 2300 |
|
80 }, |
|
81 { |
|
82 'num_chairs': 5, |
|
83 'name': u'Foobar Ltd.', |
|
84 'num_employees': 3 |
|
85 }, |
|
86 { |
|
87 'num_chairs': 34, |
|
88 'name': u'Test GmbH', |
|
89 'num_employees': 32 |
|
90 } |
|
91 ], |
|
92 lambda o: o, |
|
93 ) |
|
94 |
|
95 # Law of order of operations is followed |
|
96 company_query.update( |
|
97 num_chairs=F('num_employees') + 2 * F('num_employees') |
|
98 ) |
|
99 self.assertQuerysetEqual( |
|
100 company_query, [ |
|
101 { |
|
102 'num_chairs': 6900, |
|
103 'name': u'Example Inc.', |
|
104 'num_employees': 2300 |
|
105 }, |
|
106 { |
|
107 'num_chairs': 9, |
|
108 'name': u'Foobar Ltd.', |
|
109 'num_employees': 3 |
|
110 }, |
|
111 { |
|
112 'num_chairs': 96, |
|
113 'name': u'Test GmbH', |
|
114 'num_employees': 32 |
|
115 } |
|
116 ], |
|
117 lambda o: o, |
|
118 ) |
|
119 |
|
120 # Law of order of operations can be overridden by parentheses |
|
121 company_query.update( |
|
122 num_chairs=((F('num_employees') + 2) * F('num_employees')) |
|
123 ) |
|
124 self.assertQuerysetEqual( |
|
125 company_query, [ |
|
126 { |
|
127 'num_chairs': 5294600, |
|
128 'name': u'Example Inc.', |
|
129 'num_employees': 2300 |
|
130 }, |
|
131 { |
|
132 'num_chairs': 15, |
|
133 'name': u'Foobar Ltd.', |
|
134 'num_employees': 3 |
|
135 }, |
|
136 { |
|
137 'num_chairs': 1088, |
|
138 'name': u'Test GmbH', |
|
139 'num_employees': 32 |
|
140 } |
|
141 ], |
|
142 lambda o: o, |
|
143 ) |
|
144 |
|
145 # The relation of a foreign key can become copied over to an other |
|
146 # foreign key. |
|
147 self.assertEqual( |
|
148 Company.objects.update(point_of_contact=F('ceo')), |
|
149 3 |
|
150 ) |
|
151 self.assertQuerysetEqual( |
|
152 Company.objects.all(), [ |
|
153 "Joe Smith", |
|
154 "Frank Meyer", |
|
155 "Max Mustermann", |
|
156 ], |
|
157 lambda c: unicode(c.point_of_contact), |
|
158 ) |
|
159 |
|
160 c = Company.objects.all()[0] |
|
161 c.point_of_contact = Employee.objects.create(firstname="Guido", lastname="van Rossum") |
|
162 c.save() |
|
163 |
|
164 # F Expressions can also span joins |
|
165 self.assertQuerysetEqual( |
|
166 Company.objects.filter(ceo__firstname=F("point_of_contact__firstname")), [ |
|
167 "Foobar Ltd.", |
|
168 "Test GmbH", |
|
169 ], |
|
170 lambda c: c.name |
|
171 ) |
|
172 |
|
173 Company.objects.exclude( |
|
174 ceo__firstname=F("point_of_contact__firstname") |
|
175 ).update(name="foo") |
|
176 self.assertEqual( |
|
177 Company.objects.exclude( |
|
178 ceo__firstname=F('point_of_contact__firstname') |
|
179 ).get().name, |
|
180 "foo", |
|
181 ) |
|
182 |
|
183 self.assertRaises(FieldError, |
|
184 lambda: Company.objects.exclude( |
|
185 ceo__firstname=F('point_of_contact__firstname') |
|
186 ).update(name=F('point_of_contact__lastname')) |
|
187 ) |
|
188 |
|
189 # F expressions can be used to update attributes on single objects |
|
190 test_gmbh = Company.objects.get(name="Test GmbH") |
|
191 self.assertEqual(test_gmbh.num_employees, 32) |
|
192 test_gmbh.num_employees = F("num_employees") + 4 |
|
193 test_gmbh.save() |
|
194 test_gmbh = Company.objects.get(pk=test_gmbh.pk) |
|
195 self.assertEqual(test_gmbh.num_employees, 36) |
|
196 |
|
197 # F expressions cannot be used to update attributes which are foreign |
|
198 # keys, or attributes which involve joins. |
|
199 test_gmbh.point_of_contact = None |
|
200 test_gmbh.save() |
|
201 self.assertTrue(test_gmbh.point_of_contact is None) |
|
202 def test(): |
|
203 test_gmbh.point_of_contact = F("ceo") |
|
204 self.assertRaises(ValueError, test) |
|
205 |
|
206 test_gmbh.point_of_contact = test_gmbh.ceo |
|
207 test_gmbh.save() |
|
208 test_gmbh.name = F("ceo__last_name") |
|
209 self.assertRaises(FieldError, test_gmbh.save) |
|
210 |
|
211 # F expressions cannot be used to update attributes on objects which do |
|
212 # not yet exist in the database |
|
213 acme = Company( |
|
214 name="The Acme Widget Co.", num_employees=12, num_chairs=5, |
|
215 ceo=test_gmbh.ceo |
|
216 ) |
|
217 acme.num_employees = F("num_employees") + 16 |
|
218 self.assertRaises(TypeError, acme.save) |