diff --git a/docs/changelog.rst b/docs/changelog.rst index 04235db6..efdc1974 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -67,6 +67,7 @@ Changes in dev way the user has specified them - Fixed various errors - Added many tests +- Added pre and post bulk-insert signals Changes in v0.4 =============== diff --git a/docs/guide/signals.rst b/docs/guide/signals.rst index 58b3d6ed..47ba06be 100644 --- a/docs/guide/signals.rst +++ b/docs/guide/signals.rst @@ -9,7 +9,7 @@ Signal support is provided by the excellent `blinker`_ library and will gracefully fall back if it is not available. -The following document signals exist in MongoEngine and are pretty self explaintary: +The following document signals exist in MongoEngine and are pretty self explanatory: * `mongoengine.signals.pre_init` * `mongoengine.signals.post_init` @@ -17,6 +17,8 @@ The following document signals exist in MongoEngine and are pretty self explaint * `mongoengine.signals.post_save` * `mongoengine.signals.pre_delete` * `mongoengine.signals.post_delete` + * `mongoengine.signals.pre_bulk_insert` + * `mongoengine.signals.post_bulk_insert` Example usage:: diff --git a/mongoengine/queryset.py b/mongoengine/queryset.py index a6626855..96dc2ca6 100644 --- a/mongoengine/queryset.py +++ b/mongoengine/queryset.py @@ -1,4 +1,5 @@ from connection import _get_db +from mongoengine import signals import pprint import pymongo @@ -812,15 +813,20 @@ class QuerySet(object): raise OperationError(msg) raw.append(doc.to_mongo()) + signals.pre_bulk_insert.send(self._document, documents=docs) ids = self._collection.insert(raw) if not load_bulk: + signals.post_bulk_insert.send( + self._document, documents=docs, loaded=False) return return_one and ids[0] or ids documents = self.in_bulk(ids) results = [] for obj_id in ids: results.append(documents.get(obj_id)) + signals.post_bulk_insert.send( + self._document, documents=results, loaded=True) return return_one and results[0] or results def with_id(self, object_id): diff --git a/mongoengine/signals.py b/mongoengine/signals.py index 0a697534..52ef3129 100644 --- a/mongoengine/signals.py +++ b/mongoengine/signals.py @@ -42,3 +42,5 @@ pre_save = _signals.signal('pre_save') post_save = _signals.signal('post_save') pre_delete = _signals.signal('pre_delete') post_delete = _signals.signal('post_delete') +pre_bulk_insert = _signals.signal('pre_bulk_insert') +post_bulk_insert = _signals.signal('post_bulk_insert') diff --git a/tests/signals.py b/tests/signals.py index 9c413379..fd282b25 100644 --- a/tests/signals.py +++ b/tests/signals.py @@ -17,6 +17,7 @@ class SignalTests(unittest.TestCase): global signal_output signal_output = [] fn(*args, **kwargs) + print signal_output return signal_output def setUp(self): @@ -56,6 +57,18 @@ class SignalTests(unittest.TestCase): @classmethod def post_delete(cls, sender, document, **kwargs): signal_output.append('post_delete signal, %s' % document) + + @classmethod + def pre_bulk_insert(cls, sender, documents, **kwargs): + signal_output.append('pre_bulk_insert signal, %s' % documents) + + @classmethod + def post_bulk_insert(cls, sender, documents, **kwargs): + signal_output.append('post_bulk_insert signal, %s' % documents) + if kwargs.get('loaded', False): + signal_output.append('Is loaded') + else: + signal_output.append('Not loaded') self.Author = Author @@ -104,7 +117,9 @@ class SignalTests(unittest.TestCase): len(signals.pre_save.receivers), len(signals.post_save.receivers), len(signals.pre_delete.receivers), - len(signals.post_delete.receivers) + len(signals.post_delete.receivers), + len(signals.pre_bulk_insert.receivers), + len(signals.post_bulk_insert.receivers), ) signals.pre_init.connect(Author.pre_init, sender=Author) @@ -113,6 +128,8 @@ class SignalTests(unittest.TestCase): signals.post_save.connect(Author.post_save, sender=Author) signals.pre_delete.connect(Author.pre_delete, sender=Author) signals.post_delete.connect(Author.post_delete, sender=Author) + signals.pre_bulk_insert.connect(Author.pre_bulk_insert, sender=Author) + signals.post_bulk_insert.connect(Author.post_bulk_insert, sender=Author) signals.pre_init.connect(Another.pre_init, sender=Another) signals.post_init.connect(Another.post_init, sender=Another) @@ -128,6 +145,8 @@ class SignalTests(unittest.TestCase): signals.pre_delete.disconnect(self.Author.pre_delete) signals.post_save.disconnect(self.Author.post_save) signals.pre_save.disconnect(self.Author.pre_save) + signals.pre_bulk_insert.disconnect(self.Author.pre_bulk_insert) + signals.post_bulk_insert.disconnect(self.Author.post_bulk_insert) signals.pre_init.disconnect(self.Another.pre_init) signals.post_init.disconnect(self.Another.post_init) @@ -143,7 +162,9 @@ class SignalTests(unittest.TestCase): len(signals.pre_save.receivers), len(signals.post_save.receivers), len(signals.pre_delete.receivers), - len(signals.post_delete.receivers) + len(signals.post_delete.receivers), + len(signals.pre_bulk_insert.receivers), + len(signals.post_bulk_insert.receivers), ) self.assertEqual(self.pre_signals, post_signals) @@ -154,6 +175,14 @@ class SignalTests(unittest.TestCase): def create_author(): a1 = self.Author(name='Bill Shakespeare') + def bulk_create_author_with_load(): + a1 = self.Author(name='Bill Shakespeare') + self.Author.objects.insert([a1], load_bulk=True) + + def bulk_create_author_without_load(): + a1 = self.Author(name='Bill Shakespeare') + self.Author.objects.insert([a1], load_bulk=False) + self.assertEqual(self.get_signal_output(create_author), [ "pre_init signal, Author", "{'name': 'Bill Shakespeare'}", @@ -178,4 +207,25 @@ class SignalTests(unittest.TestCase): self.assertEqual(self.get_signal_output(a1.delete), [ 'pre_delete signal, William Shakespeare', 'post_delete signal, William Shakespeare', - ]) \ No newline at end of file + ]) + + signal_output = self.get_signal_output(bulk_create_author_with_load) + + # The output of this signal is not entirely deterministic. The reloaded + # object will have an object ID. Hence, we only check part of the output + self.assertEquals(signal_output[3], + "pre_bulk_insert signal, []") + self.assertEquals(signal_output[-2:], + ["post_bulk_insert signal, []", + "Is loaded",]) + + self.assertEqual(self.get_signal_output(bulk_create_author_without_load), [ + "pre_init signal, Author", + "{'name': 'Bill Shakespeare'}", + "post_init signal, Bill Shakespeare", + "pre_bulk_insert signal, []", + "post_bulk_insert signal, []", + "Not loaded", + ]) + + self.Author.objects.delete()