diff --git a/mongoengine/base.py b/mongoengine/base.py index c2f4d214..6b11d233 100644 --- a/mongoengine/base.py +++ b/mongoengine/base.py @@ -755,9 +755,26 @@ class BaseDocument(object): if value: continue - # If we've set a value that aint the default value save it. + # If we've set a value that ain't the default value unset it. + default = None + if path in self._fields: default = self._fields[path].default + else: # Perform a full lookup for lists / embedded lookups + d = self + parts = path.split('.') + field_name = parts.pop() + for p in parts: + if p.isdigit(): + d = d[int(p)] + elif hasattr(d, '__getattribute__'): + d = getattr(d, p) + else: + d = d.get(p) + if hasattr(d, '_fields'): + default = d._fields[field_name].default + + if default is not None: if callable(default): default = default() if default != value: diff --git a/tests/document.py b/tests/document.py index e1c536e5..146681bf 100644 --- a/tests/document.py +++ b/tests/document.py @@ -1735,6 +1735,47 @@ class DocumentTest(unittest.TestCase): self.assertEquals(person.age, 21) self.assertEquals(person.active, False) + def test_save_only_changed_fields_recursive(self): + """Ensure save only sets / unsets changed fields + """ + + class Comment(EmbeddedDocument): + published = BooleanField(default=True) + + class User(self.Person): + comments_dict = DictField() + comments = ListField(EmbeddedDocumentField(Comment)) + active = BooleanField(default=True) + + User.drop_collection() + + # Create person object and save it to the database + person = User(name='Test User', age=30, active=True) + person.comments.append(Comment()) + person.save() + person.reload() + + person = self.Person.objects.get() + self.assertTrue(person.comments[0].published) + + person.comments[0].published = False + person.save() + + person = self.Person.objects.get() + self.assertFalse(person.comments[0].published) + + # Simple dict w + person.comments_dict['first_post'] = Comment() + person.save() + + person = self.Person.objects.get() + self.assertTrue(person.comments_dict['first_post'].published) + + person.comments_dict['first_post'].published = False + person.save() + + person = self.Person.objects.get() + self.assertTrue(person.comments_dict['first_post'].published) def test_delete(self): """Ensure that document may be deleted using the delete method. """