From 07dae64d660235e681427494bd5a2d2dfc0f05dd Mon Sep 17 00:00:00 2001 From: Vincent Driessen Date: Mon, 13 Dec 2010 12:36:24 -0800 Subject: [PATCH] More the deletion code over to the QuerySet object. The Document object doens't have any delete_rule specific code anymore, and leverages the QuerySet's ability to deny/cascade/nullify its relations. --- mongoengine/document.py | 20 -------------------- mongoengine/queryset.py | 22 ++++++++++++++++++++++ tests/document.py | 30 +++++++++++++++++++++++++++++- tests/queryset.py | 15 ++++++++++++++- 4 files changed, 65 insertions(+), 22 deletions(-) diff --git a/mongoengine/document.py b/mongoengine/document.py index d89d6872..d1a031ab 100644 --- a/mongoengine/document.py +++ b/mongoengine/document.py @@ -99,26 +99,6 @@ class Document(BaseDocument): :param safe: check if the operation succeeded before returning """ - # Check for DENY rules before actually deleting/nullifying any other - # references - for rule_entry in self._meta['delete_rules']: - document_cls, field_name = rule_entry - rule = self._meta['delete_rules'][rule_entry] - if rule == DENY and document_cls.objects(**{field_name: self.id}).count() > 0: - msg = u'Could not delete document (at least %s.%s refers to it)' % \ - (document_cls.__name__, field_name) - raise OperationError(msg) - - for rule_entry in self._meta['delete_rules']: - document_cls, field_name = rule_entry - rule = self._meta['delete_rules'][rule_entry] - - if rule == CASCADE: - document_cls.objects(**{field_name: self.id}).delete(safe=safe) - elif rule == NULLIFY: - document_cls.objects(**{field_name: - self.id}).update(**{'unset__%s' % field_name: 1}) - id_field = self._meta['id_field'] object_id = self._fields[id_field].to_mongo(self[id_field]) try: diff --git a/mongoengine/queryset.py b/mongoengine/queryset.py index 49c8f69d..82efd4f7 100644 --- a/mongoengine/queryset.py +++ b/mongoengine/queryset.py @@ -882,6 +882,28 @@ class QuerySet(object): :param safe: check if the operation succeeded before returning """ + from document import CASCADE, DENY, NULLIFY + + doc = self._document + + # Check for DENY rules before actually deleting/nullifying any other + # references + for rule_entry in doc._meta['delete_rules']: + document_cls, field_name = rule_entry + rule = doc._meta['delete_rules'][rule_entry] + if rule == DENY and document_cls.objects(**{field_name + '__in': self}).count() > 0: + msg = u'Could not delete document (at least %s.%s refers to it)' % \ + (document_cls.__name__, field_name) + raise OperationError(msg) + + for rule_entry in doc._meta['delete_rules']: + document_cls, field_name = rule_entry + rule = doc._meta['delete_rules'][rule_entry] + if rule == CASCADE: + document_cls.objects(**{field_name + '__in': self}).delete(safe=safe) + elif rule == NULLIFY: + document_cls.objects(**{field_name + '__in': self}).update(**{'unset__%s' % field_name: 1}) + self._collection.remove(self._query, safe=safe) @classmethod diff --git a/tests/document.py b/tests/document.py index 11af8b22..221d22b7 100644 --- a/tests/document.py +++ b/tests/document.py @@ -661,7 +661,35 @@ class DocumentTest(unittest.TestCase): """Ensure that a chain of documents is also deleted upon cascaded deletion. """ - self.fail() + + class BlogPost(Document): + content = StringField() + author = ReferenceField(self.Person, delete_rule=CASCADE) + + class Comment(Document): + text = StringField() + post = ReferenceField(BlogPost, delete_rule=CASCADE) + + + author = self.Person(name='Test User') + author.save() + + post = BlogPost(content = 'Watched some TV') + post.author = author + post.save() + + comment = Comment(text = 'Kudos.') + comment.post = post + comment.save() + + # Delete the Person, which should lead to deletion of the BlogPost, and, + # recursively to the Comment, too + author.delete() + self.assertEqual(len(Comment.objects), 0) + + self.Person.drop_collection() + BlogPost.drop_collection() + Comment.drop_collection() def test_delete_rule_deny(self): """Ensure that a document cannot be referenced if there are still diff --git a/tests/queryset.py b/tests/queryset.py index 32bbc4bf..fecbaecc 100644 --- a/tests/queryset.py +++ b/tests/queryset.py @@ -737,7 +737,20 @@ class QuerySetTest(unittest.TestCase): def test_delete_rule_cascade(self): """Ensure cascading deletion of referring documents from the database. """ - self.fail() + class BlogPost(Document): + content = StringField() + author = ReferenceField(self.Person, delete_rule=CASCADE) + BlogPost.drop_collection() + + me = self.Person(name='Test User') + me.save() + + post = BlogPost(content='Watching TV', author=me) + post.save() + + self.assertEqual(1, BlogPost.objects.count()) + self.Person.objects.delete() + self.assertEqual(0, BlogPost.objects.count()) def test_delete_rule_nullify(self): """Ensure nullification of references to deleted documents.