1
1
import operator
2
- from datetime import timedelta
3
2
from decimal import Decimal
4
3
5
4
from django .core .exceptions import FieldDoesNotExist , ValidationError
6
- from django .db .models import ExpressionWrapper , F , IntegerField , Max , Model , Sum
5
+ from django .db .models import (
6
+ Exists ,
7
+ ExpressionWrapper ,
8
+ F ,
9
+ IntegerField ,
10
+ Max ,
11
+ Model ,
12
+ OuterRef ,
13
+ Subquery ,
14
+ Sum ,
15
+ )
7
16
from django .test import SimpleTestCase , TestCase
8
17
9
18
from django_mongodb .fields import EmbeddedModelField
16
25
DecimalParent ,
17
26
EmbeddedModel ,
18
27
EmbeddedModelFieldModel ,
28
+ Library ,
19
29
)
20
30
21
31
@@ -185,9 +195,9 @@ def test_embedded_with_json_field(self):
185
195
models [1 :3 ],
186
196
)
187
197
188
- @ staticmethod
189
- def _truncate_ms ( time ):
190
- return time - timedelta ( microseconds = time .microsecond )
198
+ def truncate_ms ( self , value ):
199
+ """Truncate microsends to millisecond precision as supported by MongoDB."""
200
+ return value . replace ( microsecond = ( value .microsecond // 1000 ) * 1000 )
191
201
192
202
################
193
203
def test_ordering_by_embedded_field (self ):
@@ -200,23 +210,26 @@ def test_ordering_by_embedded_field(self):
200
210
self .assertSequenceEqual (query , expected )
201
211
202
212
def test_ordering_grouping_by_embedded_field (self ):
213
+ expected = sorted (
214
+ (
215
+ EmbeddedModelFieldModel .objects .create (simple = EmbeddedModel (someint = x ))
216
+ for x in range (6 )
217
+ ),
218
+ key = lambda x : x .simple .someint ,
219
+ )
203
220
query = (
204
221
EmbeddedModelFieldModel .objects .annotate (
205
222
group = ExpressionWrapper (F ("simple__someint" ) + 5 , output_field = IntegerField ())
206
223
)
207
224
.values ("group" )
208
- .annotate (max_pk = Max ("simple__auto_now" ))
225
+ .annotate (max_auto_now = Max ("simple__auto_now" ))
209
226
.order_by ("simple__someint" )
210
227
)
211
- query = [{** e , "max_pk" : self ._truncate_ms (e ["max_pk" ])} for e in query ]
212
- expected = [
213
- EmbeddedModelFieldModel .objects .create (simple = EmbeddedModel (someint = x ))
214
- for x in range (6 )
215
- ]
228
+ query_response = [{** e , "max_auto_now" : self .truncate_ms (e ["max_auto_now" ])} for e in query ]
216
229
self .assertSequenceEqual (
217
- query ,
230
+ query_response ,
218
231
[
219
- {"group" : e .simple .someint + 5 , "max_pk " : self ._truncate_ms (e .simple .auto_now )}
232
+ {"group" : e .simple .someint + 5 , "max_auto_now " : self .truncate_ms (e .simple .auto_now )}
220
233
for e in expected
221
234
],
222
235
)
@@ -229,3 +242,62 @@ def test_ordering_grouping_by_sum(self):
229
242
.order_by ("sum" )
230
243
)
231
244
self .assertQuerySetEqual (qs , [0 , 2 , 4 , 6 , 8 , 10 ], operator .itemgetter ("sum" ))
245
+
246
+
247
+ class SubqueryExistsTestCase (TestCase ):
248
+ def setUp (self ):
249
+ # Create test data
250
+ address1 = Address .objects .create (city = "New York" , state = "NY" , zip_code = 10001 )
251
+ address2 = Address .objects .create (city = "Boston" , state = "MA" , zip_code = 20002 )
252
+ author1 = Author .objects .create (name = "Alice" , age = 30 , address = address1 )
253
+ author2 = Author .objects .create (name = "Bob" , age = 40 , address = address2 )
254
+ book1 = Book .objects .create (name = "Book A" , author = author1 )
255
+ book2 = Book .objects .create (name = "Book B" , author = author2 )
256
+ Book .objects .create (name = "Book C" , author = author2 )
257
+ Book .objects .create (name = "Book D" , author = author2 )
258
+ Book .objects .create (name = "Book E" , author = author1 )
259
+
260
+ library1 = Library .objects .create (
261
+ name = "Central Library" , location = "Downtown" , best_seller = "Book A"
262
+ )
263
+ library2 = Library .objects .create (
264
+ name = "Community Library" , location = "Suburbs" , best_seller = "Book A"
265
+ )
266
+
267
+ # Add books to libraries
268
+ library1 .books .add (book1 , book2 )
269
+ library2 .books .add (book2 )
270
+
271
+ def test_exists_subquery (self ):
272
+ subquery = Book .objects .filter (
273
+ author__name = OuterRef ("name" ), author__address__city = "Boston"
274
+ )
275
+ queryset = Author .objects .filter (Exists (subquery ))
276
+
277
+ self .assertEqual (queryset .count (), 1 )
278
+
279
+ def test_in_subquery (self ):
280
+ subquery = Author .objects .filter (age__gt = 35 ).values ("name" )
281
+ queryset = Book .objects .filter (author__name__in = Subquery (subquery )).order_by ("name" )
282
+
283
+ self .assertEqual (queryset .count (), 3 )
284
+ self .assertQuerySetEqual (queryset , ["Book B" , "Book C" , "Book D" ], lambda book : book .name )
285
+
286
+ def test_range_query (self ):
287
+ queryset = Author .objects .filter (age__range = (25 , 45 )).order_by ("name" )
288
+
289
+ self .assertEqual (queryset .count (), 2 )
290
+ self .assertQuerySetEqual (queryset , ["Alice" , "Bob" ], lambda author : author .name )
291
+
292
+ def test_exists_with_foreign_object (self ):
293
+ subquery = Library .objects .filter (best_seller = OuterRef ("name" ))
294
+ queryset = Book .objects .filter (Exists (subquery ))
295
+
296
+ self .assertEqual (queryset .count (), 1 )
297
+ self .assertEqual (queryset .first ().name , "Book A" )
298
+
299
+ def test_foreign_field_with_ranges (self ):
300
+ queryset = Library .objects .filter (books__author__age__range = (25 , 35 ))
301
+
302
+ self .assertEqual (queryset .count (), 1 )
303
+ self .assertEqual (queryset .first ().name , "Central Library" )
0 commit comments