Skip to content

Commit baf66e3

Browse files
committed
INTPYTHON-765 Fix crash loading EmbeddedModelField/PolymorphicEmbeddedModelField with missing fields
1 parent e747973 commit baf66e3

File tree

4 files changed

+28
-3
lines changed

4 files changed

+28
-3
lines changed

django_mongodb_backend/operations.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,8 @@ def convert_embeddedmodelfield_value(self, value, expression, connection):
184184
if value is not None:
185185
# Apply database converters to each field of the embedded model.
186186
for field in expression.output_field.embedded_model._meta.fields:
187+
if field.attname not in value:
188+
continue
187189
field_expr = Expression(output_field=field)
188190
converters = connection.ops.get_db_converters(
189191
field_expr
@@ -204,6 +206,8 @@ def convert_polymorphicembeddedmodelfield_value(self, value, expression, connect
204206
model_class = expression.output_field._get_model_from_label(value["_label"])
205207
# Apply database converters to each field of the embedded model.
206208
for field in model_class._meta.fields:
209+
if field.attname not in value:
210+
continue
207211
field_expr = Expression(output_field=field)
208212
converters = connection.ops.get_db_converters(
209213
field_expr

docs/releases/5.2.x.rst

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@ New features
1515
Bug fixes
1616
---------
1717

18-
- ...
18+
- Fixed a ``KeyError`` crash when loading ``EmbeddedModelField`` or
19+
``PolymorphicEmbeddedModelField`` if an embedded model field that uses a
20+
converter isn't present in the data (e.g. data not written by Django, or
21+
after a field was added to an existing embedded model).
1922

2023
Deprecated features
2124
-------------------

tests/model_fields_/test_embedded_model.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from datetime import timedelta
33

44
from django.core.exceptions import FieldDoesNotExist, ValidationError
5-
from django.db import models
5+
from django.db import connection, models
66
from django.db.models import (
77
Exists,
88
ExpressionWrapper,
@@ -225,6 +225,15 @@ def test_nested(self):
225225
)
226226
self.assertCountEqual(Book.objects.filter(author__address__city="NYC"), [obj])
227227

228+
def test_missing_field_in_data(self):
229+
"""
230+
Loading a model with an EmbeddedModelField that has a missing sub-field
231+
(e.g. data not written by Django) that uses a database converter (in
232+
this case, integer is an IntegerField) doesn't crash.
233+
"""
234+
connection.database.model_fields__holder.update_many({}, {"$unset": {"data.integer": ""}})
235+
self.assertIsNone(Holder.objects.first().data.integer)
236+
228237

229238
class ArrayFieldTests(TestCase):
230239
@classmethod

tests/model_fields_/test_polymorphic_embedded_model.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from decimal import Decimal
33

44
from django.core.exceptions import FieldDoesNotExist, ValidationError
5-
from django.db import models
5+
from django.db import connection, models
66
from django.test import SimpleTestCase, TestCase
77
from django.test.utils import isolate_apps
88

@@ -161,6 +161,15 @@ def test_nested(self):
161161
with self.assertRaisesMessage(FieldDoesNotExist, msg):
162162
(Person.objects.filter(pet__favorite_toy__manufacturer="Maker 1"),)
163163

164+
def test_missing_field_in_data(self):
165+
"""
166+
Loading a model with a PolymorphicEmbeddedModelField that has a missing
167+
sub-field (e.g. data not written by Django) that uses a database
168+
converter (in this case, weight is a DecimalField) doesn't crash.
169+
"""
170+
connection.database.model_fields__person.update_many({}, {"$unset": {"pet.weight": ""}})
171+
self.assertIsNone(Person.objects.first().pet.weight)
172+
164173

165174
class InvalidLookupTests(SimpleTestCase):
166175
def test_invalid_field(self):

0 commit comments

Comments
 (0)