Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Development
- BugFix - Calling .clear on a ListField wasn't being marked as changed (and flushed to db upon .save()) #2858
- Improve error message in case a document assigned to a ReferenceField wasn't saved yet #1955
- BugFix - Take `where()` into account when using `.modify()`, as in MyDocument.objects().where("this[field] >= this[otherfield]").modify(field='new') #2044
- fix: exception on null embedded scalar values

Changes in 0.29.0
=================
Expand Down
2 changes: 2 additions & 0 deletions mongoengine/queryset/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -2036,6 +2036,8 @@ def lookup(obj, name):
chunks = name.split("__")
for chunk in chunks:
obj = getattr(obj, chunk)
if obj is None:
break
return obj

data = [lookup(doc, n) for n in self._scalar]
Expand Down
30 changes: 30 additions & 0 deletions tests/queryset/test_queryset.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from mongoengine.queryset import (
DoesNotExist,
MultipleObjectsReturned,
Q,
QuerySet,
QuerySetManager,
queryset_manager,
Expand Down Expand Up @@ -4674,6 +4675,35 @@ class Person(Document):
("Gabriel Falcao", 23, "New York"),
]

def test_scalar_embedded_null_parents(self):
"""Test a multi-scalar query on embedded fields raises an exception when the parent field is null."""

class EmbeddedModelA(EmbeddedDocument):
designator = StringField()

class Container(Document):
source = EmbeddedDocumentField(EmbeddedModelA)
target = EmbeddedDocumentField(EmbeddedModelA, null=True)

# Create one with both values
Container(
source=EmbeddedModelA(designator="value1"),
target=EmbeddedModelA(designator="value2"),
).save()

# Create one with a null target, but the source value will match the query
Container(
source=EmbeddedModelA(designator="value1"),
target=None,
).save()

queryset = Container.objects.filter(
Q(source__designator="value1") | Q(target__designator="value2")
).values_list("source__designator", "target__designator")
# This should not raise an AttributeError on NoneType for the second Container's target__designator
values = list(queryset)
assert values == [("value1", "value2"), ("value1", None)]

def test_scalar_decimal(self):
from decimal import Decimal

Expand Down
Loading