Skip to content

Commit 1e930fe

Browse files
committed
Merge branch 'emilecaron-master'
2 parents c6151e3 + 4dc1585 commit 1e930fe

File tree

4 files changed

+50
-6
lines changed

4 files changed

+50
-6
lines changed

AUTHORS

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,4 +226,4 @@ that much better:
226226
* Emmanuel Leblond (https://github.com/touilleMan)
227227
* Breeze.Kay (https://github.com/9nix00)
228228
* Vicki Donchenko (https://github.com/kivistein)
229-
229+
* Emile Caron (https://github.com/emilecaron)

docs/changelog.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ Changelog
44

55
Changes in 0.10.1 - DEV
66
=======================
7+
- Fix infinite recursion with CASCADE delete rules under specific conditions. #1046
78

89
Changes in 0.10.0
910
=================

mongoengine/queryset/base.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -346,7 +346,7 @@ def count(self, with_limit_and_skip=False):
346346
return 0
347347
return self._cursor.count(with_limit_and_skip=with_limit_and_skip)
348348

349-
def delete(self, write_concern=None, _from_doc_delete=False):
349+
def delete(self, write_concern=None, _from_doc_delete=False, cascade_refs=None):
350350
"""Delete the documents matched by the query.
351351
352352
:param write_concern: Extra keyword arguments are passed down which
@@ -402,11 +402,13 @@ def delete(self, write_concern=None, _from_doc_delete=False):
402402
continue
403403
rule = doc._meta['delete_rules'][rule_entry]
404404
if rule == CASCADE:
405-
ref_q = document_cls.objects(**{field_name + '__in': self})
405+
cascade_refs = set() if cascade_refs is None else cascade_refs
406+
for ref in queryset:
407+
cascade_refs.add(ref.id)
408+
ref_q = document_cls.objects(**{field_name + '__in': self, 'id__nin': cascade_refs})
406409
ref_q_count = ref_q.count()
407-
if (doc != document_cls and ref_q_count > 0 or
408-
(doc == document_cls and ref_q_count > 0)):
409-
ref_q.delete(write_concern=write_concern)
410+
if ref_q_count > 0:
411+
ref_q.delete(write_concern=write_concern, cascade_refs=cascade_refs)
410412
elif rule == NULLIFY:
411413
document_cls.objects(**{field_name + '__in': self}).update(
412414
write_concern=write_concern, **{'unset__%s' % field_name: 1})

tests/queryset/queryset.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1413,6 +1413,47 @@ class BlogPost(AbstractBlogPost):
14131413
self.Person.objects(name='Test User').delete()
14141414
self.assertEqual(1, BlogPost.objects.count())
14151415

1416+
def test_reverse_delete_rule_cascade_cycle(self):
1417+
"""Ensure reference cascading doesn't loop if reference graph isn't
1418+
a tree
1419+
"""
1420+
class Dummy(Document):
1421+
reference = ReferenceField('self', reverse_delete_rule=CASCADE)
1422+
1423+
base = Dummy().save()
1424+
other = Dummy(reference=base).save()
1425+
base.reference = other
1426+
base.save()
1427+
1428+
base.delete()
1429+
1430+
self.assertRaises(DoesNotExist, base.reload)
1431+
self.assertRaises(DoesNotExist, other.reload)
1432+
1433+
def test_reverse_delete_rule_cascade_complex_cycle(self):
1434+
"""Ensure reference cascading doesn't loop if reference graph isn't
1435+
a tree
1436+
"""
1437+
class Category(Document):
1438+
name = StringField()
1439+
1440+
class Dummy(Document):
1441+
reference = ReferenceField('self', reverse_delete_rule=CASCADE)
1442+
cat = ReferenceField(Category, reverse_delete_rule=CASCADE)
1443+
1444+
cat = Category(name='cat').save()
1445+
base = Dummy(cat=cat).save()
1446+
other = Dummy(reference=base).save()
1447+
other2 = Dummy(reference=other).save()
1448+
base.reference = other
1449+
base.save()
1450+
1451+
cat.delete()
1452+
1453+
self.assertRaises(DoesNotExist, base.reload)
1454+
self.assertRaises(DoesNotExist, other.reload)
1455+
self.assertRaises(DoesNotExist, other2.reload)
1456+
14161457
def test_reverse_delete_rule_cascade_self_referencing(self):
14171458
"""Ensure self-referencing CASCADE deletes do not result in infinite
14181459
loop

0 commit comments

Comments
 (0)