Skip to content

Commit d6f7812

Browse files
committed
Add subquery and foreign field unit tests.
1 parent b6261c5 commit d6f7812

File tree

2 files changed

+92
-13
lines changed

2 files changed

+92
-13
lines changed

tests/model_fields_/models.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,10 @@ class Author(models.Model):
6161
class Book(models.Model):
6262
name = models.CharField(max_length=100)
6363
author = EmbeddedModelField(Author)
64+
65+
66+
class Library(models.Model):
67+
name = models.CharField(max_length=100)
68+
books = models.ManyToManyField("Book", related_name="libraries")
69+
location = models.CharField(max_length=100, null=True, blank=True)
70+
best_seller = models.CharField(max_length=100, null=True, blank=True)

tests/model_fields_/test_embedded_model.py

Lines changed: 85 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,18 @@
11
import operator
2-
from datetime import timedelta
32
from decimal import Decimal
43

54
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+
)
716
from django.test import SimpleTestCase, TestCase
817

918
from django_mongodb.fields import EmbeddedModelField
@@ -16,6 +25,7 @@
1625
DecimalParent,
1726
EmbeddedModel,
1827
EmbeddedModelFieldModel,
28+
Library,
1929
)
2030

2131

@@ -185,9 +195,9 @@ def test_embedded_with_json_field(self):
185195
models[1:3],
186196
)
187197

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)
191201

192202
################
193203
def test_ordering_by_embedded_field(self):
@@ -200,23 +210,26 @@ def test_ordering_by_embedded_field(self):
200210
self.assertSequenceEqual(query, expected)
201211

202212
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+
)
203220
query = (
204221
EmbeddedModelFieldModel.objects.annotate(
205222
group=ExpressionWrapper(F("simple__someint") + 5, output_field=IntegerField())
206223
)
207224
.values("group")
208-
.annotate(max_pk=Max("simple__auto_now"))
225+
.annotate(max_auto_now=Max("simple__auto_now"))
209226
.order_by("simple__someint")
210227
)
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]
216229
self.assertSequenceEqual(
217-
query,
230+
query_response,
218231
[
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)}
220233
for e in expected
221234
],
222235
)
@@ -229,3 +242,62 @@ def test_ordering_grouping_by_sum(self):
229242
.order_by("sum")
230243
)
231244
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

Comments
 (0)