Skip to content

Updating an inner dict within a DictField of a DynamicDocument raise an AttributeError #2795

Description

@karimelhajoui63

Hi,

Details:

I have an issue with the updating of a DictField of a DynamicDocument that sometimes raises an exception.
It seems that there is a bug in mongoengine (more precisely in the _changed_fields) when we try to update a key of an inner dict, within the DictField that is at the top level of the Document.
As far as I dug, it seems to be because the inner dict is considered as a BaseDict, so when it is updated, the inner dict is added to _changed_fields but this inner dict isn't a field of the document, so there is an AttributeError on save() (see traceback below).

One more thing: this seem to be the case only with DynamicDocument and not with Document. And there is also no exception raised when the document came from the database.

Tested on versions:

  • Python 3.10.13
  • MongoEngine 0.27.0
  • PyMongo 4.6.1

Code to reproduce the bug:

from mongoengine import connect, DictField, DynamicDocument

connect("test", host="mongodb://localhost:27018/test")

class ExampleModel(DynamicDocument):  # but there is no exception at all with `Document`
    root_dict = DictField(default=None)
    

ExampleModel.objects().delete()  # just in case

example = ExampleModel()
example.root_dict = {"inner_dict":{"key": "value"}}
example.save()  # ✅

if example.root_dict["inner_dict"]["key"]:
    print("'inner_dict' is present in 'example'")

# ------------------

# example.reload()  # <-- with this line uncommented, no exception is raised

example.root_dict["inner_dict"]["key"] = "NEW_value"
try:
    example.save()  # raise "AttributeError" ❌
except AttributeError as exc:
    print(exc)
else:
    print("No exception for 'example'")

# ------------------
    
example_from_db = ExampleModel.objects().first()
example_from_db.root_dict["inner_dict"]["key"] = "NEW_value"
try:
    example_from_db.save()  # don't raise any exception ✅
except AttributeError as exc:
    print(exc)
else:
    print("No exception for 'example_from_db'")

Output of this program:

'inner_dict' is present in 'example'
'ExampleModel' object has no attribute 'inner_dict'
No exception for 'example_from_db'

Complete traceback of the error on .save():

Traceback (most recent call last):
  File "/workspaces/bin/personal/mongoengine_dictfield.py", line 23, in <module>
    example.save()
  File "/workspaces/.venv/lib/python3.10/site-packages/mongoengine/document.py", line 429, in save
    object_id, created = self._save_update(
  File "/workspaces/.venv/lib/python3.10/site-packages/mongoengine/document.py", line 545, in _save_update
    update_doc = self._get_update_doc()
  File "/workspaces/.venv/lib/python3.10/site-packages/mongoengine/document.py", line 501, in _get_update_doc
    updates, removals = self._delta()
  File "/workspaces/.venv/lib/python3.10/site-packages/mongoengine/base/document.py", line 746, in _delta
    d = getattr(d, real_path)
AttributeError: 'ExampleModel' object has no attribute 'inner_dict'

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions