Since there is a .save() method on EmbeddedDocument, you could be tempted to attach a pre_save event to an EmbeddedDocument (#1720). This update is an attempt to make this clearer.
		
			
				
	
	
		
			150 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
	
	
			
		
		
	
	
			150 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
	
	
| .. _signals:
 | |
| 
 | |
| =======
 | |
| Signals
 | |
| =======
 | |
| 
 | |
| .. versionadded:: 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.
 | |
| 
 | |
| Overview
 | |
| --------
 | |
| 
 | |
| 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:
 | |
| 
 | |
| `pre_init`
 | |
|   Called during the creation of a new :class:`~mongoengine.Document` or
 | |
|   :class:`~mongoengine.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.
 | |
| 
 | |
| `post_init`
 | |
|   Called after all processing of a new :class:`~mongoengine.Document` or
 | |
|   :class:`~mongoengine.EmbeddedDocument` instance has been completed.
 | |
| 
 | |
| `pre_save`
 | |
|   Called within :meth:`~mongoengine.Document.save` prior to performing
 | |
|   any actions.
 | |
| 
 | |
| `pre_save_post_validation`
 | |
|   Called within :meth:`~mongoengine.Document.save` after validation
 | |
|   has taken place but before saving.
 | |
| 
 | |
| `post_save`
 | |
|   Called within :meth:`~mongoengine.Document.save` after most actions
 | |
|   (validation, insert/update, and cascades, but not clearing dirty flags) have 
 | |
|   completed successfully.  Passed the additional boolean keyword argument 
 | |
|   `created` to indicate if the save was an insert or an update.
 | |
| 
 | |
| `pre_delete`
 | |
|   Called within :meth:`~mongoengine.Document.delete` prior to
 | |
|   attempting the delete operation.
 | |
| 
 | |
| `post_delete`
 | |
|   Called within :meth:`~mongoengine.Document.delete` upon successful
 | |
|   deletion of the record.
 | |
| 
 | |
| `pre_bulk_insert`
 | |
|   Called after validation of the documents to insert, but prior to any data
 | |
|   being written. In this case, the `document` argument is replaced by a
 | |
|   `documents` argument representing the list of documents being inserted.
 | |
| 
 | |
| `post_bulk_insert`
 | |
|   Called after a successful bulk insert operation.  As per `pre_bulk_insert`,
 | |
|   the `document` argument is omitted and replaced with a `documents` argument.
 | |
|   An additional boolean argument, `loaded`, identifies the contents of
 | |
|   `documents` as either :class:`~mongoengine.Document` instances when `True` or
 | |
|   simply a list of primary key values for the inserted records if `False`.
 | |
| 
 | |
| Attaching Events
 | |
| ----------------
 | |
| 
 | |
| 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 :class:`~mongoengine.Document` or
 | |
| :class:`~mongoengine.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)
 | |
| 
 | |
| .. warning::
 | |
| 
 | |
|     Note that EmbeddedDocument only supports pre/post_init signals. pre/post_save, etc should be attached to Document's class only. Attaching pre_save to an EmbeddedDocument is ignored silently.
 | |
| 
 | |
| Finally, you can also use this small decorator to quickly create a number of
 | |
| signals and attach them to your :class:`~mongoengine.Document` or
 | |
| :class:`~mongoengine.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()
 | |
| 
 | |
| 
 | |
| .. _blinker: http://pypi.python.org/pypi/blinker
 |