From 6c0112c2be3da38e50471ffa764a64f2008ab541 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wilson=20J=C3=BAnior?= Date: Fri, 25 Jul 2014 18:12:26 -0300 Subject: [PATCH] refs #709, added support to disable auto_sync --- mongoengine/base/metaclasses.py | 9 +++-- mongoengine/fields.py | 29 ++++++++++++-- tests/fields/fields.py | 70 +++++++++++++++++++++++++++++++++ 3 files changed, 101 insertions(+), 7 deletions(-) diff --git a/mongoengine/base/metaclasses.py b/mongoengine/base/metaclasses.py index b7157a35..a4bd0144 100644 --- a/mongoengine/base/metaclasses.py +++ b/mongoengine/base/metaclasses.py @@ -31,7 +31,7 @@ class DocumentMetaclass(type): attrs['_is_document'] = attrs.get('_is_document', False) attrs['_cached_reference_fields'] = [] - + # EmbeddedDocuments could have meta data for inheritance if 'meta' in attrs: attrs['_meta'] = attrs.pop('meta') @@ -181,9 +181,12 @@ class DocumentMetaclass(type): if not f.document_type: raise InvalidDocumentError( "Document is not avaiable to sync") - + + if f.auto_sync: + f.start_listener() + f.document_type._cached_reference_fields.append(f) - + if isinstance(f, ComplexBaseField) and hasattr(f, 'field'): delete_rule = getattr(f.field, 'reverse_delete_rule', diff --git a/mongoengine/fields.py b/mongoengine/fields.py index 9b19f25d..14fcde68 100644 --- a/mongoengine/fields.py +++ b/mongoengine/fields.py @@ -989,10 +989,11 @@ class CachedReferenceField(BaseField): .. versionadded:: 0.9 """ - def __init__(self, document_type, fields=[], **kwargs): + def __init__(self, document_type, fields=[], auto_sync=True, **kwargs): """Initialises the Cached Reference Field. :param fields: A list of fields to be cached in document + :param auto_sync: if True documents are auto updated. """ if not isinstance(document_type, basestring) and \ not issubclass(document_type, (Document, basestring)): @@ -1000,10 +1001,33 @@ class CachedReferenceField(BaseField): self.error('Argument to CachedReferenceField constructor must be a' ' document class or a string') + self.auto_sync = auto_sync self.document_type_obj = document_type self.fields = fields super(CachedReferenceField, self).__init__(**kwargs) + def start_listener(self): + """ + Start listener for document alterations, and update relacted docs + """ + from mongoengine import signals + signals.post_save.connect(self.on_document_pre_save, + sender=self.document_type) + + def on_document_pre_save(self, sender, document, created, **kwargs): + if not created: + update_kwargs = { + 'set__%s__%s' % (self.name, k): v + for k, v in document._delta()[0].items() + if k in self.fields} + + if update_kwargs: + filter_kwargs = {} + filter_kwargs[self.name] = document + + self.owner_document.objects( + **filter_kwargs).update(**update_kwargs) + def to_python(self, value): """Convert a MongoDB-compatible type to a Python type. """ @@ -1088,7 +1112,6 @@ class CachedReferenceField(BaseField): def sync_all(self): update_key = 'set__%s' % self.name - errors = [] for doc in self.document_type.objects: filter_kwargs = {} @@ -1097,8 +1120,6 @@ class CachedReferenceField(BaseField): update_kwargs = {} update_kwargs[update_key] = doc - errors.append((filter_kwargs, update_kwargs)) - self.owner_document.objects( **filter_kwargs).update(**update_kwargs) diff --git a/tests/fields/fields.py b/tests/fields/fields.py index d5ae3329..77490ddb 100644 --- a/tests/fields/fields.py +++ b/tests/fields/fields.py @@ -1608,6 +1608,76 @@ class FieldTest(unittest.TestCase): self.assertRaises(InvalidDocumentError, build) + def test_cached_reference_auto_sync(self): + class Person(Document): + TYPES = ( + ('pf', "PF"), + ('pj', "PJ") + ) + name = StringField() + tp = StringField( + choices=TYPES + ) + + father = CachedReferenceField('self', fields=('tp',)) + + Person.drop_collection() + + a1 = Person(name="Wilson Father", tp="pj") + a1.save() + + a2 = Person(name='Wilson Junior', tp='pf', father=a1) + a2.save() + + a1.tp = 'pf' + a1.save() + + a2.reload() + self.assertEqual(dict(a2.to_mongo()), { + '_id': a2.pk, + 'name': 'Wilson Junior', + 'tp': 'pf', + 'father': { + '_id': a1.pk, + 'tp': 'pf' + } + }) + + def test_cached_reference_auto_sync_disabled(self): + class Persone(Document): + TYPES = ( + ('pf', "PF"), + ('pj', "PJ") + ) + name = StringField() + tp = StringField( + choices=TYPES + ) + + father = CachedReferenceField( + 'self', fields=('tp',), auto_sync=False) + + Persone.drop_collection() + + a1 = Persone(name="Wilson Father", tp="pj") + a1.save() + + a2 = Persone(name='Wilson Junior', tp='pf', father=a1) + a2.save() + + a1.tp = 'pf' + a1.save() + + self.assertEqual(Persone.objects._collection.find_one({'_id': a2.pk}), { + '_id': a2.pk, + 'name': 'Wilson Junior', + 'tp': 'pf', + 'father': { + '_id': a1.pk, + 'tp': 'pj' + } + }) + def test_cached_reference_embedded_fields(self): class Owner(EmbeddedDocument): TPS = (