From 85b9281689f0b6b3ea0f7184cafd6b78637830aa Mon Sep 17 00:00:00 2001 From: Chris Targett Date: Sun, 16 Apr 2017 13:41:13 +0100 Subject: [PATCH] Added support for schemaless/empty {'type': 'object'} fields. --- jsonmodels/builders.py | 2 ++ jsonmodels/fields.py | 9 ++++++++- tests/fixtures/schema_empty_object.json | 24 ++++++++++++++++++++++++ tests/test_jsonmodels.py | 18 ++++++++++++++++++ tests/test_schema.py | 16 ++++++++++++++++ 5 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 tests/fixtures/schema_empty_object.json diff --git a/jsonmodels/builders.py b/jsonmodels/builders.py index 7274e11..151d018 100644 --- a/jsonmodels/builders.py +++ b/jsonmodels/builders.py @@ -142,6 +142,8 @@ def build(self): return {'type': 'number'} elif issubclass(self.type, float): return {'type': 'number'} + elif self.type == dict: + return {'type': 'object'} raise errors.FieldNotSupported( "Can't specify value schema!", self.type diff --git a/jsonmodels/fields.py b/jsonmodels/fields.py index c478495..b38b655 100644 --- a/jsonmodels/fields.py +++ b/jsonmodels/fields.py @@ -304,7 +304,14 @@ def _get_embed_type(self): return self.types[0] def to_struct(self, value): - return value.to_struct() + try: + return value.to_struct() + except AttributeError: + # Only when we specify `dict` as a type should + # we return the raw value. + if dict not in self.types: + raise + return value.copy() class _LazyType(object): diff --git a/tests/fixtures/schema_empty_object.json b/tests/fixtures/schema_empty_object.json new file mode 100644 index 0000000..4ba9158 --- /dev/null +++ b/tests/fixtures/schema_empty_object.json @@ -0,0 +1,24 @@ +{ + "additionalProperties": false, + "definitions": { + "tests_test_schema_structured": { + "additionalProperties": false, + "properties": { + "key": {"type": "string"}, + "value": {"type": "string"} + }, + "type": "object" + } + }, + "properties": { + "arbitrary": {"type": "object"}, + "structured": "#/definitions/tests_test_schema_structured", + "type_choices": { + "oneOf": [ + "#/definitions/tests_test_schema_structured", + {"type": "object"} + ] + } + }, + "type": "object" +} diff --git a/tests/test_jsonmodels.py b/tests/test_jsonmodels.py index 2d8ea61..df9db65 100644 --- a/tests/test_jsonmodels.py +++ b/tests/test_jsonmodels.py @@ -574,3 +574,21 @@ class Model(models.Base): assert Model(age=1) == Model(age=1) assert Model(age=1) != Model(age=2) assert Model(name='William', age=1) != Model(age=1) + + +def test_empty_object(): + class Model(models.Base): + data = fields.EmbeddedField(dict) + + inst = Model(data={'wiz': 'boing'}) + + assert inst.data == {'wiz': 'boing'} + + +def test_empty_object_to_struct(): + class Model(models.Base): + data = fields.EmbeddedField(dict) + + inst = Model(data={'wiz': 'boing'}) + + assert inst.to_struct() == {'data': {'wiz': 'boing'}} diff --git a/tests/test_schema.py b/tests/test_schema.py index 9efaf77..548519a 100644 --- a/tests/test_schema.py +++ b/tests/test_schema.py @@ -371,3 +371,19 @@ class Person(models.Base): with pytest.raises(errors.FieldNotSupported): Person.to_json_schema() + + +def test_schema_empty_object(): + class Structured(models.Base): + key = fields.StringField() + value = fields.StringField() + + class DataContainer(models.Base): + arbitrary = fields.EmbeddedField(dict) + structured = fields.EmbeddedField(Structured) + type_choices = fields.EmbeddedField([Structured, dict]) + + schema = DataContainer.to_json_schema() + + pattern = get_fixture('schema_empty_object.json') + assert compare_schemas(pattern, schema)