Merge pull request #1 from emilecaron/fix_delete_rule_cascade_cycle

Fix delete rule cascade cycle
This commit is contained in:
Emile Caron 2015-06-25 21:53:04 +02:00
commit 13e0a1b5bb
2 changed files with 25 additions and 4 deletions

View File

@ -346,7 +346,7 @@ class BaseQuerySet(object):
return 0 return 0
return self._cursor.count(with_limit_and_skip=with_limit_and_skip) return self._cursor.count(with_limit_and_skip=with_limit_and_skip)
def delete(self, write_concern=None, _from_doc_delete=False): def delete(self, write_concern=None, _from_doc_delete=False, cascade_refs=None):
"""Delete the documents matched by the query. """Delete the documents matched by the query.
:param write_concern: Extra keyword arguments are passed down which :param write_concern: Extra keyword arguments are passed down which
@ -363,6 +363,11 @@ class BaseQuerySet(object):
queryset = self.clone() queryset = self.clone()
doc = queryset._document doc = queryset._document
cascade_refs = set() if cascade_refs is None else cascade_refs
if doc in cascade_refs:
return 0
cascade_refs.add(doc)
if write_concern is None: if write_concern is None:
write_concern = {} write_concern = {}
@ -404,9 +409,8 @@ class BaseQuerySet(object):
if rule == CASCADE: if rule == CASCADE:
ref_q = document_cls.objects(**{field_name + '__in': self}) ref_q = document_cls.objects(**{field_name + '__in': self})
ref_q_count = ref_q.count() ref_q_count = ref_q.count()
if (doc != document_cls and ref_q_count > 0 or if ref_q_count > 0:
(doc == document_cls and ref_q_count > 0)): ref_q.delete(write_concern=write_concern, cascade_refs=cascade_refs)
ref_q.delete(write_concern=write_concern)
elif rule == NULLIFY: elif rule == NULLIFY:
document_cls.objects(**{field_name + '__in': self}).update( document_cls.objects(**{field_name + '__in': self}).update(
write_concern=write_concern, **{'unset__%s' % field_name: 1}) write_concern=write_concern, **{'unset__%s' % field_name: 1})

View File

@ -1413,6 +1413,23 @@ class QuerySetTest(unittest.TestCase):
self.Person.objects(name='Test User').delete() self.Person.objects(name='Test User').delete()
self.assertEqual(1, BlogPost.objects.count()) self.assertEqual(1, BlogPost.objects.count())
def test_reverse_delete_rule_cascade_cycle(self):
"""Ensure reference cascading doesn't loop if reference graph isn't
a tree
"""
class Dummy(Document):
reference = ReferenceField('self', reverse_delete_rule=CASCADE)
base = Dummy().save()
other = Dummy(reference=base).save()
base.reference = other
base.save()
base.delete()
self.assertRaises(DoesNotExist, base.reload)
self.assertRaises(DoesNotExist, other.reload)
def test_reverse_delete_rule_cascade_self_referencing(self): def test_reverse_delete_rule_cascade_self_referencing(self):
"""Ensure self-referencing CASCADE deletes do not result in infinite """Ensure self-referencing CASCADE deletes do not result in infinite
loop loop