From 5bb63f645ba04e5731ae95dfd3154be6a3792faa Mon Sep 17 00:00:00 2001 From: Harry Marr Date: Fri, 8 Jun 2012 19:24:10 +0200 Subject: [PATCH 1/3] Fix minor typo w/ FloatField --- mongoengine/fields.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mongoengine/fields.py b/mongoengine/fields.py index c72c6cb4..04da7447 100644 --- a/mongoengine/fields.py +++ b/mongoengine/fields.py @@ -182,7 +182,7 @@ class FloatField(BaseField): if isinstance(value, int): value = float(value) if not isinstance(value, float): - self.error('FoatField only accepts float values') + self.error('FloatField only accepts float values') if self.min_value is not None and value < self.min_value: self.error('Float value is too small') From 2d9b581f34324ac2759452fb5a839282ca35f361 Mon Sep 17 00:00:00 2001 From: Shaun Duncan Date: Fri, 15 Jun 2012 15:42:19 -0400 Subject: [PATCH 2/3] Adding check if cascade delete is self-referencing. If so, prevent recursing if there are no objects to evaluate --- mongoengine/queryset.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mongoengine/queryset.py b/mongoengine/queryset.py index 1fb3eda7..8370d333 100644 --- a/mongoengine/queryset.py +++ b/mongoengine/queryset.py @@ -1314,7 +1314,9 @@ class QuerySet(object): 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) + ref_q = document_cls.objects(**{field_name + '__in': self}) + if doc != document_cls or (doc == document_cls and ref_q.count() > 0): + ref_q.delete(safe=safe) elif rule == NULLIFY: document_cls.objects(**{field_name + '__in': self}).update( safe_update=safe, From 2ec1476e50e748de205b4c1721205f50b2bc574e Mon Sep 17 00:00:00 2001 From: Shaun Duncan Date: Sat, 16 Jun 2012 11:05:23 -0400 Subject: [PATCH 3/3] Adding test case for self-referencing documents with cascade deletes --- tests/queryset.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tests/queryset.py b/tests/queryset.py index 3b662489..c768ade9 100644 --- a/tests/queryset.py +++ b/tests/queryset.py @@ -1344,6 +1344,37 @@ class QuerySetTest(unittest.TestCase): self.Person.objects(name='Test User').delete() self.assertEqual(1, BlogPost.objects.count()) + def test_reverse_delete_rule_cascade_self_referencing(self): + """Ensure self-referencing CASCADE deletes do not result in infinite loop + """ + class Category(Document): + name = StringField() + parent = ReferenceField('self', reverse_delete_rule=CASCADE) + + num_children = 3 + base = Category(name='Root') + base.save() + + # Create a simple parent-child tree + for i in range(num_children): + child_name = 'Child-%i' % i + child = Category(name=child_name, parent=base) + child.save() + + for i in range(num_children): + child_child_name = 'Child-Child-%i' % i + child_child = Category(name=child_child_name, parent=child) + child_child.save() + + tree_size = 1 + num_children + (num_children * num_children) + self.assertEquals(tree_size, Category.objects.count()) + self.assertEquals(num_children, Category.objects(parent=base).count()) + + # The delete should effectively wipe out the Category collection + # without resulting in infinite parent-child cascade recursion + base.delete() + self.assertEquals(0, Category.objects.count()) + def test_reverse_delete_rule_nullify(self): """Ensure nullification of references to deleted documents. """