diff --git a/docs/changelog.rst b/docs/changelog.rst index 7682598b..6fb7e6aa 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -5,6 +5,7 @@ Changelog Changes in 0.10.7 - DEV ======================= - Fixed not being able to specify `use_db_field=False` on `ListField(EmbeddedDocumentField)` instances +- Fixed cascade delete mixing among collections #1224 Changes in 0.10.6 ================= diff --git a/mongoengine/queryset/base.py b/mongoengine/queryset/base.py index 454d781a..de796de5 100644 --- a/mongoengine/queryset/base.py +++ b/mongoengine/queryset/base.py @@ -403,8 +403,10 @@ class BaseQuerySet(object): rule = doc._meta['delete_rules'][rule_entry] if rule == CASCADE: cascade_refs = set() if cascade_refs is None else cascade_refs - for ref in queryset: - cascade_refs.add(ref.id) + # Handle recursive reference + if doc._collection == document_cls._collection: + for ref in queryset: + cascade_refs.add(ref.id) ref_q = document_cls.objects(**{field_name + '__in': self, 'id__nin': cascade_refs}) ref_q_count = ref_q.count() if ref_q_count > 0: diff --git a/tests/document/instance.py b/tests/document/instance.py index 61f13897..4cfe8b8e 100644 --- a/tests/document/instance.py +++ b/tests/document/instance.py @@ -1906,6 +1906,62 @@ class InstanceTest(unittest.TestCase): author.delete() self.assertEqual(BlogPost.objects.count(), 0) + def test_reverse_delete_rule_with_custom_id_field(self): + """Ensure that a referenced document with custom primary key + is also deleted upon deletion. + """ + class User(Document): + name = StringField(primary_key=True) + + class Book(Document): + author = ReferenceField(User, reverse_delete_rule=CASCADE) + reviewer = ReferenceField(User, reverse_delete_rule=NULLIFY) + + User.drop_collection() + Book.drop_collection() + + user = User(name='Mike').save() + reviewer = User(name='John').save() + book = Book(author=user, reviewer=reviewer).save() + + reviewer.delete() + self.assertEqual(Book.objects.count(), 1) + self.assertEqual(Book.objects.get().reviewer, None) + + user.delete() + self.assertEqual(Book.objects.count(), 0) + + def test_reverse_delete_rule_with_shared_id_among_collections(self): + """Ensure that cascade delete rule doesn't mix id among collections. + """ + class User(Document): + id = IntField(primary_key=True) + + class Book(Document): + id = IntField(primary_key=True) + author = ReferenceField(User, reverse_delete_rule=CASCADE) + + User.drop_collection() + Book.drop_collection() + + user_1 = User(id=1).save() + user_2 = User(id=2).save() + book_1 = Book(id=1, author=user_2).save() + book_2 = Book(id=2, author=user_1).save() + + user_2.delete() + # Deleting user_2 should also delete book_1 but not book_2 + self.assertEqual(Book.objects.count(), 1) + self.assertEqual(Book.objects.get(), book_2) + + user_3 = User(id=3).save() + book_3 = Book(id=3, author=user_3).save() + + user_3.delete() + # Deleting user_3 should also delete book_3 + self.assertEqual(Book.objects.count(), 1) + self.assertEqual(Book.objects.get(), book_2) + def test_reverse_delete_rule_with_document_inheritance(self): """Ensure that a referenced document is also deleted upon deletion of a child document.