-
Notifications
You must be signed in to change notification settings - Fork 62
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ReferenceField lazy/auto dereference #42
Comments
It's a design choice ! # using asyncio
async def get_ref_a(doc):
referenced = await doc.ref.fetch()
return referenced.a Beside, I like the idea of explicitness in fetching data from the database (this way you easily know how much you hit the database in a single request, which is pretty nice for optimizing) Regarding your particular usecase, you can add a property on the document @instance.register
class MyDoc(Document):
ref = fields.ReferenceField(ReferencedDocument)
@property
def ref_fetched(self):
return self.ref.fetch()
referenced = ReferencedDocument(a=42)
my_doc = MyDoc(ref=referenced)
assert my_doc.ref_fetched.a == 42 Another more generic possibility would be to subclass To do that you need to register your new builder with I've just pushed a patch (3c679c1) to make the overloading of a builder possible. From this, maybe we could provide your |
Thanks for this. I'll have a look at it. |
I tried the PyMongoReference subclass approach as you explained. To get it to work, I also had to create a AFAIU, self.BUILDER_CLS = import_module('umongo.frameworks.pymongo').PyMongoBuilder |
(Also, I'm not sure you need to overload |
For the record, here's what I have in my application code (I put the whole thing in application code and I don't depend on the branch in #43):
# -*- coding: utf-8 -*-
"""Custom PyMongoInstance"""
from umongo.fields import ReferenceField
from umongo.frameworks.pymongo import PyMongoReference, PyMongoBuilder
from umongo.instance import LazyLoaderInstance
class PyMongoReferenceAutoFetch(PyMongoReference):
def __init__(self, document_cls, pk):
# Attribute getter/setter/deleter are overridden to provide
# direct access to referenced document. We must use __dict__
# directly to get around those.
self.__dict__['document_cls'] = document_cls
self.__dict__['pk'] = pk
self.__dict__['_document'] = None
def __getitem__(self, name):
return self.fetch()[name]
def __setitem__(self, name, value):
self.fetch()[name] = value
def __delitem__(self, name):
del self.fetch()[name]
def __getattr__(self, name):
if name in self.__dict__:
return self.__dict__[name]
return getattr(self.fetch(), name)
def __setattr__(self, name, value):
if name in self.__dict__:
self.__dict__[name] = value
else:
setattr(self.fetch(), name)
def __delattr__(self, name):
if name in self.__dict__:
del self.__dict__[name]
else:
delattr(self.fetch(), name)
class PyMongoBuilderAutoFetch(PyMongoBuilder):
def _patch_field(self, field):
super()._patch_field(field)
if isinstance(field, ReferenceField):
field.reference_cls = PyMongoReferenceAutoFetch
class PyMongoInstanceAutoFetch(LazyLoaderInstance):
"""
:class:`umongo.instance.LazyLoaderInstance` implementation for pymongo
"""
def __init__(self, *args, **kwargs):
self.BUILDER_CLS = PyMongoBuilderAutoFetch
super().__init__(*args, **kwargs) I still think this |
umongo.frameworks.register_builder(PyMongoBuilderAutoFetch) # do this somewhere before creating instance
db = MongoClient().test
instance = umongo.Instance(db) Beside, I'm thinking of adding this as a bonus for the pymongo driver... stay tunned ! PS: you do not need the header |
Thank you for your feedback on this. I just realized that:
Overall, I'm leaning to your side. Rather make things explicit than try to hide complexity and create dangerous corner cases. Anyone wanting to pick this up, keep this in mind.
OK maybe your implementation will be better and more useful than mine. I'm leaving this open, then.
Thanks for the tip. |
Let's close this for now and keep fetch manual. The conversation is interesting. It provides a way to do auto-dereferencing and what issues to expect. |
Currently, a
ReferenceField
deserializes as (for instance) aPyMongoReference
, and must be dereferenced manually usingfetch()
.This is not as practical as automatic dereferencing (systematic or lazy as in MongoEngine).
Is this a design choice (simplicity) or something that you'd like to see improved?
In practice, I could call
fetch()
every time I'm accessing a ref field, but there are cases where you need to pass an object to another method you can't modify (imported library) and you really wantdoc.ref
to be the referenced document, not aPyMongoReference
.And I can't even dereference every reference manually to get a "clean" object:
I'm kinda stuck. Is there a way around this? Am I missing something?
The text was updated successfully, but these errors were encountered: