Description

Simple models allow to make structured dict-like (json serializable) models for your application.

Main goals

  • Make declarative structures based on dict – easy to understand, convenient to support;
  • Implement structured data models with low coupling – use anywhere;
  • Should be able to validate structured data – more expected behaviour for tons of code;
  • Simplified code which contains big nested structures especially for API integration;
  • Convenient way to use in IDE – auto-complete using attributes instead dict keys;

Basics

Simple example

from simplemodels.fields import SimpleField
from simplemodels.models import DictEmbeddedDocument


class Address(DictEmbeddedDocument):
    city = SimpleField(default='Saint-Petersburg')
    street = SimpleField()


class Person(DictEmbeddedDocument):
    name = SimpleField(required=True)
    address = SimpleField(type=Address)
    insurance_number = SimpleField(type=int)  # object typing


address = Address(street='Nevskii prospect 10')


# pure dict structure for any compatibility
>>> address
{'city': 'Saint-Petersburg', 'street': 'Nevskii prospect 10'}


# dict subclass, nothing special
>>> address.items()
[('city', 'Saint-Petersburg'), ('street', 'Nevskii prospect 10')]


# validation is supported
>>> person = Person()
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  ***
ValidationError: Field 'name' is required for Person


# or another example with wrong nested structure
>>> person = Person(name='Max', address='St.Petersburg, Nevskii prospect 10')
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  ***
ValidationError: Wrong value instance, should be 'Address'


# correct behaviour
>>> person = Person(name='Max', address=Address(street='Nevskii prospect 10'), insurance_number='111')
>>> person
{'name': 'Max', 'address': {'city': 'Saint-Petersburg', 'street': 'Nevskii prospect 10'}, 'insurance_number': 111}


class Address(DictEmbeddedDocument):
    city = SimpleField(required=True)
    street = SimpleField()


# This example will raise an ValidationError !
>>> person = Person(name='Max', address={'town': 'Saint-Petersburg'})

# But if pass correct structure, then all will be ok
>>> person = Person(name='Max', address={'city': 'Saint-Petersburg'})
>>> isinstance(person.address, Address)
True

Simple field

There is only one field type - SimpleField which stores any value. You can add require or/and validation for it

class simplemodels.fields.SimpleField(default=None, required=False, choices=None, type=None, validator=None, error_text='', name=None, **kwargs)

Class-field with descriptor for DictEmbeddedDocument

__init__(default=None, required=False, choices=None, type=None, validator=None, error_text='', name=None, **kwargs)
Parameters:
  • name – optional name
  • default – default value
  • required – is field required
  • choices – choices list. See utils.Choices
  • type – type validation
  • validator – enhanced type validation with callable object
  • error_text – return with validation error, which helps to debug
  • kwargs – for future options
__weakref__

list of weak references to the object (if defined)

validate(value)

Helper method to validate field. It could be done with 2 ways:

  • built-in validator with validator.validate(value) method
  • provided by user custom callable method to validate
Parameters:value
Returns:

Fields type validation and validators

From version 0.2.0 fields type validation is supported. See test examples below.

From version 0.2.1:
  • old validation ways with type are DEPRECATED. Use validator instead (see below)
  • DictEmbeddedDocument.get_instance method is DEPRECATED, use direct constructor instead
from simplemodels.fields import SimpleField
from simplemodels.models import DictEmbeddedDocument

class PostAddress(DictEmbeddedDocument):
    street = SimpleField(type=str)

class Person(DictEmbeddedDocument):
    id = SimpleField(type=int)
    name = SimpleField(required=True, default='TestName')
    address = SimpleField(type=PostAddress)

person_1 = Person(id='1', name='Maks', address=PostAddress(street=999))

person_2 = Person(id='2', name='John', address=dict(street=999))

# NOTE: take a look at `id` and `address.street`. All values will be casted to selected `type`
self.assertIsInstance(person_1, Person)
self.assertEqual(person_1.id, 1)
self.assertEqual(person_1.address.street, '999')  # type casting will be applied

self.assertEqual(person_2.address.street, '999')  # type casting will be applied for dict value as well

New field validation (starts from 0.2.1)

New validator attribute has been added to SimpleField:

  • It must be callable
  • Use method from_dict for related DictEmbeddedDocument models
class PostAddress(DictEmbeddedDocument):
    city = SimpleField(validator=str)  # same behaviour as previous
    delivery_date = SimpleField(       # advanced validator for datetime or whatever
        validator=lambda value: datetime.strptime(
            value, '%Y-%m-%dT%H:%M:%SZ'))

class Package(DictEmbeddedDocument):
    id = SimpleField(validator=int)
    address = SimpleField(validator=PostAddress.from_dict)  # validate address

Optional field name (starts from 0.2.4)

Feature enables to use optional field name with custom characters.

class MyModel(DictEmbeddedDocument):
    InterestRate = SimpleField(validator=float,
                               name='Interest Rate',
                               required=True)
    ...

This will match a message like

{
    "Country": "Germany",
    "Currency": "EUR",
    "GDP": "34,388",
    "Inflation": "2.2",
    "Interest Rate": "1.01",
    "Population": "80,767,000"
}