Because mongoengine defines document structure in python classes you can use normal python class @property
decorated methods to return calculated values.
Consider the following example:
import mongoengine as mdb
mdb.connect("so-37396173")
class User(mdb.Document):
name = mdb.StringField()
class UserConnection(mdb.Document):
user_a = mdb.ReferenceField('User')
user_b = mdb.ReferenceField('User')
services = mdb.ListField(mdb.ReferenceField('Service'))
@property
def weight(self):
return sum([s.weight for s in self.services])
class Component(mdb.EmbeddedDocument):
name = mdb.StringField()
weight = mdb.FloatField()
class Service(mdb.Document):
name = mdb.StringField()
components = mdb.EmbeddedDocumentListField('Component')
@property
def weight(self):
return sum([c.weight for c in self.components])
When you have a UserConnection
object you can then access a weight
attribute:
>>> uc = UserConnection.objects.first()
>>> uc.weight
0.8544546532
The drawback of this is that weight
is never stored on the database in the context of a UserConnection
so you can't aggregate it or sort on it at that level but the aggregation framework may provide some good options. If you need to have the weight saved then you could define some signals to include it before the document is saved:
import mongoengine as mdb
mdb.connect("so-37396173")
class User(mdb.Document):
name = mdb.StringField()
class UserConnection(mdb.Document):
user_a = mdb.ReferenceField('User')
user_b = mdb.ReferenceField('User')
services = mdb.ListField(mdb.ReferenceField('Service'))
weight = mdb.FloatField()
@classmethod
def calc_weight(cls, sender, document, **kwargs):
document.weight = sum([s.weight for s in document.services])
mdb.signals.pre_save.connect(UserConnection.calc_weight, sender=UserConnection)
class Component(mdb.EmbeddedDocument):
name = mdb.StringField()
weight = mdb.FloatField()
class Service(mdb.Document):
name = mdb.StringField()
components = mdb.EmbeddedDocumentListField('Component')
weight = mdb.FloatField()
@classmethod
def calc_weight(cls, sender, document, **kwargs):
document.weight = sum([s.weight for s in document.components])
mdb.signals.pre_save.connect(Service.calc_weight, sender=Service)
A drawback with this is that you have to call the save
method which means doing update
with an upsert=True
won't create the weights.
Whether you use embedded vs. reference depends on what else you want to do with the data.