Skip to content

Commit e50799e

Browse files
committed
Merge branch 'master' of github.com:MongoEngine/mongoengine into fix_cls_in_constructor
2 parents e315583 + b92c484 commit e50799e

File tree

3 files changed

+41
-19
lines changed

3 files changed

+41
-19
lines changed

docs/changelog.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ Development
66
===========
77
- (Fill this out as you fix issues and develop your features).
88
- Fix `_cls` that is not set properly in Document constructor (regression) #1950
9+
- Fix bug in _delta method - Update of a ListField depends on an unrelated dynamic field update #1733
910
- Remove deprecated `save()` method and used `insert_one()` #1899
1011

1112
=================

mongoengine/base/document.py

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -582,7 +582,6 @@ def _delta(self):
582582

583583
set_fields = self._get_changed_fields()
584584
unset_data = {}
585-
parts = []
586585
if hasattr(self, '_changed_fields'):
587586
set_data = {}
588587
# Fetch each set item from its path
@@ -592,15 +591,13 @@ def _delta(self):
592591
new_path = []
593592
for p in parts:
594593
if isinstance(d, (ObjectId, DBRef)):
594+
# Don't dig in the references
595595
break
596-
elif isinstance(d, list) and p.lstrip('-').isdigit():
597-
if p[0] == '-':
598-
p = str(len(d) + int(p))
599-
try:
600-
d = d[int(p)]
601-
except IndexError:
602-
d = None
596+
elif isinstance(d, list) and p.isdigit():
597+
# An item of a list (identified by its index) is updated
598+
d = d[int(p)]
603599
elif hasattr(d, 'get'):
600+
# dict-like (dict, embedded document)
604601
d = d.get(p)
605602
new_path.append(p)
606603
path = '.'.join(new_path)
@@ -612,26 +609,26 @@ def _delta(self):
612609

613610
# Determine if any changed items were actually unset.
614611
for path, value in set_data.items():
615-
if value or isinstance(value, (numbers.Number, bool)):
612+
if value or isinstance(value, (numbers.Number, bool)): # Account for 0 and True that are truthy
616613
continue
617614

618-
# If we've set a value that ain't the default value don't unset it.
619-
default = None
615+
parts = path.split('.')
616+
620617
if (self._dynamic and len(parts) and parts[0] in
621618
self._dynamic_fields):
622619
del set_data[path]
623620
unset_data[path] = 1
624621
continue
625-
elif path in self._fields:
622+
623+
# If we've set a value that ain't the default value don't unset it.
624+
default = None
625+
if path in self._fields:
626626
default = self._fields[path].default
627627
else: # Perform a full lookup for lists / embedded lookups
628628
d = self
629-
parts = path.split('.')
630629
db_field_name = parts.pop()
631630
for p in parts:
632-
if isinstance(d, list) and p.lstrip('-').isdigit():
633-
if p[0] == '-':
634-
p = str(len(d) + int(p))
631+
if isinstance(d, list) and p.isdigit():
635632
d = d[int(p)]
636633
elif (hasattr(d, '__getattribute__') and
637634
not isinstance(d, dict)):
@@ -649,10 +646,9 @@ def _delta(self):
649646
default = None
650647

651648
if default is not None:
652-
if callable(default):
653-
default = default()
649+
default = default() if callable(default) else default
654650

655-
if default != value:
651+
if value != default:
656652
continue
657653

658654
del set_data[path]

tests/fields/fields.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,31 @@ class Person(Document):
186186
data_to_be_saved = sorted(person.to_mongo().keys())
187187
self.assertEqual(data_to_be_saved, ['age', 'created', 'userid'])
188188

189+
def test_default_value_is_not_used_when_changing_value_to_empty_list_for_strict_doc(self):
190+
"""List field with default can be set to the empty list (strict)"""
191+
# Issue #1733
192+
class Doc(Document):
193+
x = ListField(IntField(), default=lambda: [42])
194+
195+
doc = Doc(x=[1]).save()
196+
doc.x = []
197+
doc.save()
198+
reloaded = Doc.objects.get(id=doc.id)
199+
self.assertEqual(reloaded.x, [])
200+
201+
def test_default_value_is_not_used_when_changing_value_to_empty_list_for_dyn_doc(self):
202+
"""List field with default can be set to the empty list (dynamic)"""
203+
# Issue #1733
204+
class Doc(DynamicDocument):
205+
x = ListField(IntField(), default=lambda: [42])
206+
207+
doc = Doc(x=[1]).save()
208+
doc.x = []
209+
doc.y = 2 # Was triggering the bug
210+
doc.save()
211+
reloaded = Doc.objects.get(id=doc.id)
212+
self.assertEqual(reloaded.x, [])
213+
189214
def test_default_values_when_deleting_value(self):
190215
"""Ensure that default field values are used after non-default
191216
values are explicitly deleted.

0 commit comments

Comments
 (0)