# -*- coding: utf-8 -*- from __future__ import absolute_import import datetime as dt import decimal from datetime import datetime import mongoengine as me from marshmallow import validate, Schema import pytest from marshmallow_mongoengine import (fields, fields_for_model, ModelSchema, ModelConverter, convert_field, field_for) TEST_DB = 'marshmallow_mongoengine-test' db = me.connect(TEST_DB) class BaseTest(object): @classmethod def setup_method(self, method): # Reset database from previous test run db.drop_database(TEST_DB) def contains_validator(field, v_type): for v in field.validators: if isinstance(v, v_type): return v return False class TestFields(BaseTest): def test_FileField(self): class File(me.Document): name = me.StringField(primary_key=True) file = me.FileField() class FileSchema(ModelSchema): class Meta: model = File doc = File(name='test_file') data = b'1234567890' * 10 doc.file.put(data, content_type='application/octet-stream') dump = FileSchema().dump(doc) assert not dump.errors assert dump.data == {'name': 'test_file'} # Should not be able to load the file load = FileSchema().load({'name': 'bad_load', 'file': b'12345'}) assert not load.data.file def test_ListField(self): class Doc(me.Document): list = me.ListField(me.StringField()) fields_ = fields_for_model(Doc) assert type(fields_['list']) is fields.List class DocSchema(ModelSchema): class Meta: model = Doc list_ = ['A', 'B', 'C'] doc = Doc(list=list_) dump = DocSchema().dump(doc) assert not dump.errors assert dump.data == {'list': list_} load = DocSchema().load(dump.data) assert not load.errors assert load.data.list == list_ def test_ListSpecialField(self): class NestedDoc(me.EmbeddedDocument): field = me.StringField() class Doc(me.Document): list = me.ListField(me.EmbeddedDocumentField(NestedDoc)) fields_ = fields_for_model(Doc) assert type(fields_['list']) is fields.List assert type(fields_['list'].container) is fields.Nested class DocSchema(ModelSchema): class Meta: model = Doc list_ = [{'field': 'A'}, {'field': 'B'}, {'field': 'C'}] doc = Doc(list=list_) dump = DocSchema().dump(doc) assert not dump.errors assert dump.data == {'list': list_} load = DocSchema().load(dump.data) assert not load.errors for i, elem in enumerate(list_): assert load.data.list[i].field == elem['field'] def test_DictField(self): class Doc(me.Document): data = me.DictField() fields_ = fields_for_model(Doc) assert type(fields_['data']) is fields.Raw class DocSchema(ModelSchema): class Meta: model = Doc data = { 'int_1': 1, 'nested_2': { 'sub_int_1': 42, 'sub_list_2': [] }, 'list_3': ['a', 'b', 'c'] } doc = Doc(data=data) dump = DocSchema().dump(doc) assert not dump.errors assert dump.data == {'data': data} load = DocSchema().load(dump.data) assert not load.errors assert load.data.data == data def test_DynamicField(self): class Doc(me.Document): dynamic = me.DynamicField() fields_ = fields_for_model(Doc) assert type(fields_['dynamic']) is fields.Raw class DocSchema(ModelSchema): class Meta: model = Doc data = { 'int_1': 1, 'nested_2': { 'sub_int_1': 42, 'sub_list_2': [] }, 'list_3': ['a', 'b', 'c'] } doc = Doc(dynamic=data) dump = DocSchema().dump(doc) assert not dump.errors assert dump.data == {'dynamic': data} load = DocSchema().load(dump.data) assert not load.errors assert load.data.dynamic == data def test_GenericReferenceField(self): class Doc(me.Document): id = me.StringField(primary_key=True, default='main') generic = me.GenericReferenceField() class SubDocA(me.Document): field_a = me.StringField(primary_key=True, default='doc_a_pk') class SubDocB(me.Document): field_b = me.IntField(primary_key=True, default=42) fields_ = fields_for_model(Doc) assert type(fields_['generic']) is fields.GenericReference class DocSchema(ModelSchema): class Meta: model = Doc # Test dump sub_doc_a = SubDocA().save() sub_doc_b = SubDocB().save() doc = Doc(generic=sub_doc_a) dump = DocSchema().dump(doc) assert not dump.errors assert dump.data == {'generic': 'doc_a_pk', 'id': 'main'} doc.generic = sub_doc_b doc.save() dump = DocSchema().dump(doc) assert not dump.errors assert dump.data == {'generic': 42, 'id': 'main'} # Test load for bad_generic in ( {'id': str(sub_doc_a.id)}, {'_cls': sub_doc_a._class_name}, {'id': str(sub_doc_a.id), '_cls': sub_doc_b._class_name}, {'id': 'not_an_id', '_cls': sub_doc_a._class_name}, {'id': 42, '_cls': sub_doc_a._class_name}, {'id': 'main', '_cls': sub_doc_b._class_name}, {'id': str(sub_doc_a.id), '_cls': 'not_a_class'}, {'id': None, '_cls': sub_doc_a._class_name}, {'id': str(sub_doc_a.id), '_cls': None}, ): load = DocSchema().load({"generic": bad_generic}) assert 'generic' in load.errors load = DocSchema().load({"generic": {"id": str(sub_doc_a.id), "_cls": sub_doc_a._class_name}}) assert not load.errors assert load.data['generic'] == sub_doc_a load = DocSchema().load({"generic": {"id": str(sub_doc_b.id), "_cls": sub_doc_b._class_name}}) assert not load.errors assert load.data['generic'] == sub_doc_b # Teste choices param class DocOnlyA(me.Document): id = me.StringField(primary_key=True, default='main') generic = me.GenericReferenceField(choices=[SubDocA]) class DocOnlyASchema(ModelSchema): class Meta: model = DocOnlyA load = DocOnlyASchema().load({}) assert not load.errors load = DocOnlyASchema().load({"generic": {"id": str(sub_doc_a.id), "_cls": sub_doc_a._class_name}}) assert not load.errors assert load.data['generic'] == sub_doc_a load = DocOnlyASchema().load({"generic": {"id": str(sub_doc_b.id), "_cls": sub_doc_b._class_name}}) assert 'generic' in load.errors def test_GenericEmbeddedDocumentField(self): class Doc(me.Document): id = me.StringField(primary_key=True, default='main') embedded = me.GenericEmbeddedDocumentField() class EmbeddedA(me.EmbeddedDocument): field_a = me.StringField(default='field_a_value') class EmbeddedB(me.EmbeddedDocument): field_b = me.IntField(default=42) fields_ = fields_for_model(Doc) assert type(fields_['embedded']) is fields.GenericEmbeddedDocument class DocSchema(ModelSchema): class Meta: model = Doc doc = Doc(embedded=EmbeddedA()) dump = DocSchema().dump(doc) assert not dump.errors assert dump.data == {'embedded': {'field_a': 'field_a_value'}, 'id': 'main'} doc.embedded = EmbeddedB() doc.save() dump = DocSchema().dump(doc) assert not dump.errors assert dump.data == {'embedded': {'field_b': 42}, 'id': 'main'} # TODO: test load ? def test_MapField(self): class MappedDoc(me.EmbeddedDocument): field = me.StringField() class Doc(me.Document): id = me.IntField(primary_key=True, default=1) map = me.MapField(me.EmbeddedDocumentField(MappedDoc)) str = me.MapField(me.StringField()) fields_ = fields_for_model(Doc) assert type(fields_['map']) is fields.Map class DocSchema(ModelSchema): class Meta: model = Doc doc = Doc(map={'a': MappedDoc(field='A'), 'b': MappedDoc(field='B')}, str={'a': 'aaa', 'b': 'bbbb'}).save() dump = DocSchema().dump(doc) assert not dump.errors assert dump.data == {'map': {'a': {'field': 'A'}, 'b': {'field': 'B'}}, 'str': {'a': 'aaa', 'b': 'bbbb'}, 'id': 1} # Try the load load = DocSchema().load(dump.data) assert not load.errors assert load.data.map == doc.map def test_ReferenceField(self): class ReferenceDoc(me.Document): field = me.IntField(primary_key=True, default=42) class Doc(me.Document): id = me.StringField(primary_key=True, default='main') ref = me.ReferenceField(ReferenceDoc) fields_ = fields_for_model(Doc) assert type(fields_['ref']) is fields.Reference class DocSchema(ModelSchema): class Meta: model = Doc ref_doc = ReferenceDoc().save() doc = Doc(ref=ref_doc) dump = DocSchema().dump(doc) assert not dump.errors assert dump.data == {'ref': 42, 'id': 'main'} # Try the same with reference document type passed as string class DocSchemaRefAsString(Schema): id = fields.String() ref = fields.Reference('ReferenceDoc') dump = DocSchemaRefAsString().dump(doc) assert not dump.errors assert dump.data == {'ref': 42, 'id': 'main'} # Test the field loading load = DocSchemaRefAsString().load(dump.data) assert not load.errors assert type(load.data['ref']) == ReferenceDoc # Try invalid loads for bad_ref in (1, 'NaN', None): dump.data['ref'] = bad_ref _, errors = DocSchemaRefAsString().load(dump.data) assert errors, bad_ref def test_PointField(self): class Doc(me.Document): point = me.PointField() class DocSchema(ModelSchema): class Meta: model = Doc doc = Doc(point={ 'type': 'Point', 'coordinates': [10, 20] }) dump = DocSchema().dump(doc) assert not dump.errors assert dump.data['point'] == { 'x': 10, 'y': 20 } load = DocSchema().load(dump.data) assert not load.errors assert load.data.point == { 'type': 'Point', 'coordinates': [10, 20] } # Deserialize Point with coordinates passed as string data = {'point': { 'x': '10', 'y': '20' }} load = DocSchema().load(data) assert not load.errors assert load.data.point == { 'type': 'Point', 'coordinates': [10, 20] } # Try to load invalid coordinates data = {'point': { 'x': '10', 'y': '20foo' }} load = DocSchema().load(data) assert 'point' in load.errors