Skip to content

Commit

Permalink
Merge pull request #2488 from bagerard/add_lazy_loading_method_ease_p…
Browse files Browse the repository at this point in the history
…rofiling

Add _lazy_load_ref methods to make profiling easier
  • Loading branch information
bagerard committed Mar 8, 2021
2 parents 86ad8d1 + 58a3c6d commit 74ceb97
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 36 deletions.
25 changes: 17 additions & 8 deletions mongoengine/base/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,17 @@ def __init__(self, field=None, **kwargs):
self.field = field
super().__init__(**kwargs)

@staticmethod
def _lazy_load_refs(instance, name, ref_values, *, max_depth):
_dereference = _import_class("DeReference")()
documents = _dereference(
ref_values,
max_depth=max_depth,
instance=instance,
name=name,
)
return documents

def __get__(self, instance, owner):
"""Descriptor to automatically dereference references."""
if instance is None:
Expand All @@ -284,19 +295,15 @@ def __get__(self, instance, owner):
or isinstance(self.field, (GenericReferenceField, ReferenceField))
)

_dereference = _import_class("DeReference")()

if (
instance._initialised
and dereference
and instance._data.get(self.name)
and not getattr(instance._data[self.name], "_dereferenced", False)
):
instance._data[self.name] = _dereference(
instance._data.get(self.name),
max_depth=1,
instance=instance,
name=self.name,
ref_values = instance._data.get(self.name)
instance._data[self.name] = self._lazy_load_refs(
ref_values=ref_values, instance=instance, name=self.name, max_depth=1
)
if hasattr(instance._data[self.name], "_dereferenced"):
instance._data[self.name]._dereferenced = True
Expand All @@ -322,7 +329,9 @@ def __get__(self, instance, owner):
and isinstance(value, (BaseList, BaseDict))
and not value._dereferenced
):
value = _dereference(value, max_depth=1, instance=instance, name=self.name)
value = self._lazy_load_refs(
ref_values=value, instance=instance, name=self.name, max_depth=1
)
value._dereferenced = True
instance._data[self.name] = value

Expand Down
62 changes: 34 additions & 28 deletions mongoengine/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -1194,27 +1194,32 @@ def document_type(self):
self.document_type_obj = get_document(self.document_type_obj)
return self.document_type_obj

@staticmethod
def _lazy_load_ref(ref_cls, dbref):
dereferenced_son = ref_cls._get_db().dereference(dbref)
if dereferenced_son is None:
raise DoesNotExist(f"Trying to dereference unknown document {dbref}")

return ref_cls._from_son(dereferenced_son)

def __get__(self, instance, owner):
"""Descriptor to allow lazy dereferencing."""
if instance is None:
# Document class being used rather than a document object
return self

# Get value from document instance if available
value = instance._data.get(self.name)
ref_value = instance._data.get(self.name)
auto_dereference = instance._fields[self.name]._auto_dereference
# Dereference DBRefs
if auto_dereference and isinstance(value, DBRef):
if hasattr(value, "cls"):
if auto_dereference and isinstance(ref_value, DBRef):
if hasattr(ref_value, "cls"):
# Dereference using the class type specified in the reference
cls = get_document(value.cls)
cls = get_document(ref_value.cls)
else:
cls = self.document_type
dereferenced = cls._get_db().dereference(value)
if dereferenced is None:
raise DoesNotExist("Trying to dereference unknown document %s" % value)
else:
instance._data[self.name] = cls._from_son(dereferenced)

instance._data[self.name] = self._lazy_load_ref(cls, ref_value)

return super().__get__(instance, owner)

Expand Down Expand Up @@ -1353,6 +1358,14 @@ def document_type(self):
self.document_type_obj = get_document(self.document_type_obj)
return self.document_type_obj

@staticmethod
def _lazy_load_ref(ref_cls, dbref):
dereferenced_son = ref_cls._get_db().dereference(dbref)
if dereferenced_son is None:
raise DoesNotExist(f"Trying to dereference unknown document {dbref}")

return ref_cls._from_son(dereferenced_son)

def __get__(self, instance, owner):
if instance is None:
# Document class being used rather than a document object
Expand All @@ -1364,11 +1377,7 @@ def __get__(self, instance, owner):

# Dereference DBRefs
if auto_dereference and isinstance(value, DBRef):
dereferenced = self.document_type._get_db().dereference(value)
if dereferenced is None:
raise DoesNotExist("Trying to dereference unknown document %s" % value)
else:
instance._data[self.name] = self.document_type._from_son(dereferenced)
instance._data[self.name] = self._lazy_load_ref(self.document_type, value)

return super().__get__(instance, owner)

Expand Down Expand Up @@ -1493,19 +1502,24 @@ def _validate_choices(self, value):
value = value._class_name
super()._validate_choices(value)

@staticmethod
def _lazy_load_ref(ref_cls, dbref):
dereferenced_son = ref_cls._get_db().dereference(dbref)
if dereferenced_son is None:
raise DoesNotExist(f"Trying to dereference unknown document {dbref}")

return ref_cls._from_son(dereferenced_son)

def __get__(self, instance, owner):
if instance is None:
return self

value = instance._data.get(self.name)

auto_dereference = instance._fields[self.name]._auto_dereference
if auto_dereference and isinstance(value, (dict, SON)):
dereferenced = self.dereference(value)
if dereferenced is None:
raise DoesNotExist("Trying to dereference unknown document %s" % value)
else:
instance._data[self.name] = dereferenced
if auto_dereference and isinstance(value, dict):
doc_cls = get_document(value["_cls"])
instance._data[self.name] = self._lazy_load_ref(doc_cls, value["_ref"])

return super().__get__(instance, owner)

Expand All @@ -1524,14 +1538,6 @@ def validate(self, value):
" saved to the database"
)

def dereference(self, value):
doc_cls = get_document(value["_cls"])
reference = value["_ref"]
doc = doc_cls._get_db().dereference(reference)
if doc is not None:
doc = doc_cls._from_son(doc)
return doc

def to_mongo(self, document):
if document is None:
return None
Expand Down

0 comments on commit 74ceb97

Please sign in to comment.