Skip to content

Commit 34abcc4

Browse files
committed
INTPYTHON-602 Fix QuerySet results of embedded model fields that use database converters
1 parent 41cc35f commit 34abcc4

File tree

4 files changed

+45
-1
lines changed

4 files changed

+45
-1
lines changed

django_mongodb_backend/operations.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,8 @@ def get_db_converters(self, expression):
107107
converters.append(self.convert_datetimefield_value)
108108
elif internal_type == "DecimalField":
109109
converters.append(self.convert_decimalfield_value)
110+
elif internal_type == "EmbeddedModelField":
111+
converters.append(self.convert_embeddedmodelfield_value)
110112
elif internal_type == "JSONField":
111113
converters.append(self.convert_jsonfield_value)
112114
elif internal_type == "TimeField":
@@ -150,6 +152,16 @@ def convert_durationfield_value(self, value, expression, connection):
150152
value = datetime.timedelta(milliseconds=int(str(value)))
151153
return value
152154

155+
def convert_embeddedmodelfield_value(self, value, expression, connection):
156+
if value is not None:
157+
# Apply database converters to each field of the embedded model.
158+
for field in expression.output_field.embedded_model._meta.fields:
159+
field_expr = Expression(output_field=field)
160+
converters = connection.ops.get_db_converters(field_expr)
161+
for converter in converters:
162+
value[field.attname] = converter(value[field.attname], field_expr, connection)
163+
return value
164+
153165
def convert_jsonfield_value(self, value, expression, connection):
154166
"""
155167
Convert dict data to a string so that JSONField.from_db_value() can

docs/source/releases/5.1.x.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ Django MongoDB Backend 5.1.x
1313
- Fixed the results of queries that use the ``tzinfo`` parameter of the
1414
``Trunc`` database functions.
1515
- Added support for ``QuerySet.dates()`` and ``datetimes()``.
16+
- Fixed loading of ``QuerySet`` results for embedded models that have fields
17+
that use database converters. For example, a crash for ``DecimalField``:
18+
``ValidationError: ['“1” value must be a decimal number.']``).
1619

1720
.. _django-mongodb-backend-5.1.0-beta-2:
1821

tests/model_fields_/models.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,12 @@ class Data(EmbeddedModel):
104104
auto_now = models.DateTimeField(auto_now=True)
105105
auto_now_add = models.DateTimeField(auto_now_add=True)
106106
json_value = models.JSONField()
107+
decimal = models.DecimalField(max_digits=9, decimal_places="2", null=True, blank=True)
108+
nested_data = EmbeddedModelField("NestedData", null=True, blank=True)
109+
110+
111+
class NestedData(EmbeddedModel):
112+
decimal = models.DecimalField(max_digits=9, decimal_places="2", null=True, blank=True)
107113

108114

109115
class Address(EmbeddedModel):

tests/model_fields_/test_embedded_model.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
Data,
2525
Holder,
2626
Library,
27+
NestedData,
2728
)
2829
from .utils import truncate_ms
2930

@@ -93,7 +94,16 @@ def test_pre_save(self):
9394
class QueryingTests(TestCase):
9495
@classmethod
9596
def setUpTestData(cls):
96-
cls.objs = [Holder.objects.create(data=Data(integer=x)) for x in range(6)]
97+
cls.objs = [
98+
Holder.objects.create(
99+
data=Data(
100+
integer=x,
101+
decimal=f"{x}.5",
102+
nested_data=NestedData(decimal=f"{x}.5"),
103+
)
104+
)
105+
for x in range(6)
106+
]
97107

98108
def test_exact(self):
99109
self.assertCountEqual(Holder.objects.filter(data__integer=3), [self.objs[3]])
@@ -113,6 +123,19 @@ def test_gte(self):
113123
def test_range(self):
114124
self.assertCountEqual(Holder.objects.filter(data__integer__range=(2, 4)), self.objs[2:5])
115125

126+
def test_exact_decimal(self):
127+
# EmbeddedModelField lookups call
128+
# DatabaseOperations.adapt__<foo>field_value().
129+
self.assertCountEqual(Holder.objects.filter(data__decimal="3.5"), [self.objs[3]])
130+
131+
def test_lt_decimal(self):
132+
self.assertCountEqual(Holder.objects.filter(data__decimal__lt="3"), self.objs[0:3])
133+
134+
def test_exact_decimal_nested(self):
135+
self.assertCountEqual(
136+
Holder.objects.filter(data__nested_data__decimal="3.5"), [self.objs[3]]
137+
)
138+
116139
def test_order_by_embedded_field(self):
117140
qs = Holder.objects.filter(data__integer__gt=3).order_by("-data__integer")
118141
self.assertSequenceEqual(qs, list(reversed(self.objs[4:])))

0 commit comments

Comments
 (0)