Source code for mongoengine_goodjson.fields.follow_reference

#!/usr/bin/env python
# coding=utf-8

"""Follow Reference Field code."""

import logging

from bson import SON
import mongoengine as db
from ..document import Document
from ..utils import method_dispatch, id_first

log = logging.getLogger(__name__)


[docs]class FollowReferenceField(db.ReferenceField): """ Follow Reference Field. This field can be treated as a field like ReferenceField, but generates the JSON/dict of the referenced document like embedded document. Note: This field doesn't check recursion level. Therefore, please be careful for self-referenced document. """ def __init__(self, *args, **kwargs): """ Initialize the class. Parameters: *args, **kwsrgs: Any arguments to be passed to ReferenceField. Keyword Arguments: id_check: Set False to disable id check. By default, this value is True autosave: Set True to save/update the referenced document when to_python is called. max_depth: Set natural value to set depath limit for loop-reference. By default this value is set to 3. """ self.id_check = kwargs.pop("id_check", True) self.autosave = kwargs.pop("autosave", False) self.max_depth = kwargs.pop("max_depth", None) or 3 self.parent_docs = {} super(FollowReferenceField, self).__init__(*args, **kwargs) if isinstance(self.max_depth, int) and \ self.max_depth < 0 and self.document_type_obj is \ db.fields.RECURSIVE_REFERENCE_CONSTANT: log.warn( "[BE CAREFUL!] Unlimited self reference might cause " "infinity loop! [BE CAREFUL!]" ) def __get_doc_parent_pair(self, document, **kwargs): """Return document that this field has.""" doc = document if isinstance(doc, db.Document): if doc.pk is None and self.id_check: self.error("The referenced document needs ID.") else: try: doc = self.document_type.objects( pk=super( FollowReferenceField, self ).to_mongo(document, **kwargs) ).get() except db.DoesNotExist: doc = None return doc @method_dispatch def __get_doc_dict(self, doc, **kwargs): kwargs.pop("cur_depth", None) kwargs.pop("good_json", None) return doc.to_mongo(**kwargs) @__get_doc_dict.register(Document) def __get_gjdoc_dict(self, doc, **kwargs): return doc.to_mongo(**kwargs)
[docs] def to_mongo(self, document, **kwargs): """ Convert to python-typed dict. Parameters: document: The document. """ doc = self.__get_doc_parent_pair(document, **kwargs) cur_depth = getattr(self, "$$cur_depth$$", None) max_depth = self.max_depth stop = False try: stop = max_depth(doc, cur_depth) except TypeError: stop = all([ max_depth > -1, isinstance(cur_depth, int) and cur_depth >= max_depth ]) stop = (cur_depth is None) or stop if stop: return super( FollowReferenceField, self ).to_mongo(document, **kwargs) ret = self.__get_doc_dict( doc, cur_depth=cur_depth + 1, good_json=True, **kwargs ) if issubclass(self.document_type, Document): ret.setdefault("id", ret.pop("_id", None)) return id_first(ret)
[docs] def to_python(self, value): """ Convert to python-based object. Parameters: value: The python-typed document. """ clone = value if isinstance(value, dict): clone = self.document_type._from_son( SON(value), created="id" not in value ) if self.autosave: clone.save() ret = super(FollowReferenceField, self).to_python(clone) return ret