Merge branch 'emilecaron-master'
This commit is contained in:
commit
1e930fe950
2
AUTHORS
2
AUTHORS
@ -226,4 +226,4 @@ that much better:
|
|||||||
* Emmanuel Leblond (https://github.com/touilleMan)
|
* Emmanuel Leblond (https://github.com/touilleMan)
|
||||||
* Breeze.Kay (https://github.com/9nix00)
|
* Breeze.Kay (https://github.com/9nix00)
|
||||||
* Vicki Donchenko (https://github.com/kivistein)
|
* Vicki Donchenko (https://github.com/kivistein)
|
||||||
|
* Emile Caron (https://github.com/emilecaron)
|
||||||
|
@ -4,6 +4,7 @@ Changelog
|
|||||||
|
|
||||||
Changes in 0.10.1 - DEV
|
Changes in 0.10.1 - DEV
|
||||||
=======================
|
=======================
|
||||||
|
- Fix infinite recursion with CASCADE delete rules under specific conditions. #1046
|
||||||
|
|
||||||
Changes in 0.10.0
|
Changes in 0.10.0
|
||||||
=================
|
=================
|
||||||
|
@ -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
|
||||||
@ -402,11 +402,13 @@ class BaseQuerySet(object):
|
|||||||
continue
|
continue
|
||||||
rule = doc._meta['delete_rules'][rule_entry]
|
rule = doc._meta['delete_rules'][rule_entry]
|
||||||
if rule == CASCADE:
|
if rule == CASCADE:
|
||||||
ref_q = document_cls.objects(**{field_name + '__in': self})
|
cascade_refs = set() if cascade_refs is None else cascade_refs
|
||||||
|
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()
|
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})
|
||||||
|
@ -1413,6 +1413,47 @@ 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_complex_cycle(self):
|
||||||
|
"""Ensure reference cascading doesn't loop if reference graph isn't
|
||||||
|
a tree
|
||||||
|
"""
|
||||||
|
class Category(Document):
|
||||||
|
name = StringField()
|
||||||
|
|
||||||
|
class Dummy(Document):
|
||||||
|
reference = ReferenceField('self', reverse_delete_rule=CASCADE)
|
||||||
|
cat = ReferenceField(Category, reverse_delete_rule=CASCADE)
|
||||||
|
|
||||||
|
cat = Category(name='cat').save()
|
||||||
|
base = Dummy(cat=cat).save()
|
||||||
|
other = Dummy(reference=base).save()
|
||||||
|
other2 = Dummy(reference=other).save()
|
||||||
|
base.reference = other
|
||||||
|
base.save()
|
||||||
|
|
||||||
|
cat.delete()
|
||||||
|
|
||||||
|
self.assertRaises(DoesNotExist, base.reload)
|
||||||
|
self.assertRaises(DoesNotExist, other.reload)
|
||||||
|
self.assertRaises(DoesNotExist, other2.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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user