Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Development
This should have a negative impact on performance of count see Issue #2219
- Fix a bug that made the queryset drop the read_preference after clone().
- Remove Py3.5 from CI as it reached EOL and add Python 3.9
- Fix some issues related with db_field conflict in constructor #2414
- Fix the behavior of Doc.objects.limit(0) which should return all documents (similar to mongodb) #2311
- Bug fix in ListField when updating the first item, it was saving the whole list, instead of
just replacing the first item (as it's usually done) #2392
Expand Down
17 changes: 10 additions & 7 deletions mongoengine/base/document.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,12 +101,13 @@ def __init__(self, *args, **values):

self._dynamic_fields = SON()

# Assign default values to the instance.
for key, field in self._fields.items():
if self._db_field_map.get(key, key) in values:
# Assign default values for fields
# not set in the constructor
for field_name in self._fields:
if field_name in values:
continue
value = getattr(self, key, None)
setattr(self, key, value)
value = getattr(self, field_name, None)
setattr(self, field_name, value)

if "_cls" not in values:
self._cls = self._class_name
Expand All @@ -115,7 +116,6 @@ def __init__(self, *args, **values):
dynamic_data = {}
FileField = _import_class("FileField")
for key, value in values.items():
key = self._reverse_db_field_map.get(key, key)
field = self._fields.get(key)
if field or key in ("id", "pk", "_cls"):
if __auto_convert and value is not None:
Expand Down Expand Up @@ -750,7 +750,8 @@ def _get_collection_name(cls):

@classmethod
def _from_son(cls, son, _auto_dereference=True, created=False):
"""Create an instance of a Document (subclass) from a PyMongo SON."""
"""Create an instance of a Document (subclass) from a PyMongo SON (dict)
"""
if son and not isinstance(son, dict):
raise ValueError(
"The source SON object needs to be of type 'dict' but a '%s' was found"
Expand All @@ -763,6 +764,8 @@ def _from_son(cls, son, _auto_dereference=True, created=False):

# Convert SON to a data dict, making sure each key is a string and
# corresponds to the right db field.
# This is needed as _from_son is currently called both from BaseDocument.__init__
# and from EmbeddedDocumentField.to_python
data = {}
for key, value in son.items():
key = str(key)
Expand Down
90 changes: 90 additions & 0 deletions tests/document/test_instance.py
Original file line number Diff line number Diff line change
Expand Up @@ -3822,5 +3822,95 @@ class Book(Document):
assert book._object_key == {"pk": book.pk, "author__name": "Author"}


class DBFieldMappingTest(MongoDBTestCase):
def setUp(self):
class Fields(object):
w1 = BooleanField(db_field="w2")

x1 = BooleanField(db_field="x2")
x2 = BooleanField(db_field="x3")

y1 = BooleanField(db_field="y0")
y2 = BooleanField(db_field="y1")

z1 = BooleanField(db_field="z2")
z2 = BooleanField(db_field="z1")

class Doc(Fields, Document):
pass

class DynDoc(Fields, DynamicDocument):
pass

self.Doc = Doc
self.DynDoc = DynDoc

def tearDown(self):
for collection in list_collection_names(self.db):
self.db.drop_collection(collection)

def test_setting_fields_in_constructor_of_strict_doc_uses_model_names(self):
doc = self.Doc(z1=True, z2=False)
assert doc.z1 is True
assert doc.z2 is False

def test_setting_fields_in_constructor_of_dyn_doc_uses_model_names(self):
doc = self.DynDoc(z1=True, z2=False)
assert doc.z1 is True
assert doc.z2 is False

def test_setting_unknown_field_in_constructor_of_dyn_doc_does_not_overwrite_model_fields(
self,
):
doc = self.DynDoc(w2=True)
assert doc.w1 is None
assert doc.w2 is True

def test_unknown_fields_of_strict_doc_do_not_overwrite_dbfields_1(self):
doc = self.Doc()
doc.w2 = True
doc.x3 = True
doc.y0 = True
doc.save()
reloaded = self.Doc.objects.get(id=doc.id)
assert reloaded.w1 is None
assert reloaded.x1 is None
assert reloaded.x2 is None
assert reloaded.y1 is None
assert reloaded.y2 is None

def test_dbfields_are_loaded_to_the_right_modelfield_for_strict_doc_2(self):
doc = self.Doc()
doc.x2 = True
doc.y2 = True
doc.z2 = True
doc.save()
reloaded = self.Doc.objects.get(id=doc.id)
assert (
reloaded.x1,
reloaded.x2,
reloaded.y1,
reloaded.y2,
reloaded.z1,
reloaded.z2,
) == (doc.x1, doc.x2, doc.y1, doc.y2, doc.z1, doc.z2)

def test_dbfields_are_loaded_to_the_right_modelfield_for_dyn_doc_2(self):
doc = self.DynDoc()
doc.x2 = True
doc.y2 = True
doc.z2 = True
doc.save()
reloaded = self.DynDoc.objects.get(id=doc.id)
assert (
reloaded.x1,
reloaded.x2,
reloaded.y1,
reloaded.y2,
reloaded.z1,
reloaded.z2,
) == (doc.x1, doc.x2, doc.y1, doc.y2, doc.z1, doc.z2)


if __name__ == "__main__":
unittest.main()
7 changes: 7 additions & 0 deletions tests/fields/test_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -2272,6 +2272,13 @@ class Doc(Document):
with pytest.raises(FieldDoesNotExist):
Doc(bar="test")

def test_undefined_field_works_no_confusion_with_db_field(self):
class Doc(Document):
foo = StringField(db_field="bar")

with pytest.raises(FieldDoesNotExist):
Doc(bar="test")


class TestEmbeddedDocumentListField(MongoDBTestCase):
def setUp(self):
Expand Down