diff --git a/.gitignore b/.gitignore index 14313d8..0ac5a8b 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ # Packages *.egg +*.eggs *.egg-info dist build diff --git a/jsonmodels/fields.py b/jsonmodels/fields.py index e900611..6d1a8f0 100644 --- a/jsonmodels/fields.py +++ b/jsonmodels/fields.py @@ -5,7 +5,7 @@ import re import six from dateutil.parser import parse -from typing import List, Optional, Dict, Set +from typing import List, Optional, Dict, Set, Union, Pattern from .collections import ModelCollection from .errors import RequiredFieldError, BadTypeError, AmbiguousTypeError @@ -14,6 +14,12 @@ # it is a completely valid default value. NotSet = object() +# BSON compatible types, which can be returned by toBsonEncodable. +BsonEncodable = Union[ + float, str, object, Dict, List, bytes, bool, datetime.datetime, None, + Pattern, int, bytes +] + class BaseField(object): @@ -125,8 +131,26 @@ def _get_embed_type(value, models): return matching_models[0] return models[0] + def toBsonEncodable(self, value: types) -> BsonEncodable: + """Optionally return a bson encodable python object. + + Returned object should be BSON compatible. By default uses the + `to_struct` method, which creates JSON compatible types. JSON is + compatible with bson, but only has support for limited types. When + required, this method should cast the value to supported bson type. + See: https://api.mongodb.com/python/current/api/bson/index.html + + For example: when a value is a datetime object return it as a datetime + object. When a value is of CustomDateObject, cast it to a datetime + object before returning it. + + :param value: Value + :return: a value which should be bson encodable + """ + return self.to_struct(value=value) + def to_struct(self, value): - """Cast value to Python structure.""" + """Cast value to Python dict.""" return value def parse_value(self, value): diff --git a/tests/test_plain.py b/tests/test_plain.py new file mode 100644 index 0000000..5736409 --- /dev/null +++ b/tests/test_plain.py @@ -0,0 +1,11 @@ +from unittest import mock + +from jsonmodels import fields + + +@mock.patch('jsonmodels.fields.BaseField.to_struct') +def test_toBsonEncodable_calls_to_struct(f): + """Test if default implementation of toBsonEncodable calls to_struct.""" + field = fields.StringField() + field.toBsonEncodable(value="text") + f.assert_called_once()