New in version 0.5.
Note
Signal support is provided by the excellent blinker library. If you wish to enable signal support this library must be installed, though it is not required for MongoEngine to function.
Signals are found within the mongoengine.signals module. Unless specified signals receive no additional arguments beyond the sender class and document instance. Post-signals are only called if there were no exceptions raised during the processing of their related function.
Available signals include:
Document
or
EmbeddedDocument
instance, after the constructor
arguments have been collected but before any additional processing has been
done to them. (I.e. assignment of default values.) Handlers for this signal
are passed the dictionary of arguments using the values keyword argument
and may modify this dictionary prior to returning.Document
or
EmbeddedDocument
instance has been completed.save()
prior to performing
any actions.save()
after validation
has taken place but before saving.save()
after all actions
(validation, insert/update, cascades, clearing dirty flags) have completed
successfully. Passed the additional boolean keyword argument created to
indicate if the save was an insert or an update.delete()
prior to
attempting the delete operation.delete()
upon successful
deletion of the record.Document
instances when True or
simply a list of primary key values for the inserted records if False.After writing a handler function like the following:
import logging
from datetime import datetime
from mongoengine import *
from mongoengine import signals
def update_modified(sender, document):
document.modified = datetime.utcnow()
You attach the event handler to your Document
or
EmbeddedDocument
subclass:
class Record(Document):
modified = DateTimeField()
signals.pre_save.connect(update_modified)
While this is not the most elaborate document model, it does demonstrate the concepts involved. As a more complete demonstration you can also define your handlers within your subclass:
class Author(Document):
name = StringField()
@classmethod
def pre_save(cls, sender, document, **kwargs):
logging.debug("Pre Save: %s" % document.name)
@classmethod
def post_save(cls, sender, document, **kwargs):
logging.debug("Post Save: %s" % document.name)
if 'created' in kwargs:
if kwargs['created']:
logging.debug("Created")
else:
logging.debug("Updated")
signals.pre_save.connect(Author.pre_save, sender=Author)
signals.post_save.connect(Author.post_save, sender=Author)
Finally, you can also use this small decorator to quickly create a number of
signals and attach them to your Document
or
EmbeddedDocument
subclasses as class decorators:
def handler(event):
"""Signal decorator to allow use of callback functions as class decorators."""
def decorator(fn):
def apply(cls):
event.connect(fn, sender=cls)
return cls
fn.apply = apply
return fn
return decorator
Using the first example of updating a modification time the code is now much cleaner looking while still allowing manual execution of the callback:
@handler(signals.pre_save)
def update_modified(sender, document):
document.modified = datetime.utcnow()
@update_modified.apply
class Record(Document):
modified = DateTimeField()
Currently reverse_delete_rule does not trigger signals on the other part of the relationship. If this is required you must manually handle the reverse deletion.