{"id":13770961,"url":"https://github.com/prawn-cake/simple-models","last_synced_at":"2025-05-06T22:25:40.942Z","repository":{"id":13950070,"uuid":"16650124","full_name":"prawn-cake/simple-models","owner":"prawn-cake","description":"Simple models - keep your API messages in shape with validation and handy descriptors based object interface","archived":false,"fork":false,"pushed_at":"2019-06-17T19:19:34.000Z","size":316,"stargazers_count":14,"open_issues_count":2,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-19T15:35:54.164Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/prawn-cake.png","metadata":{"files":{"readme":"README.md","changelog":"Changelog.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-02-08T18:42:57.000Z","updated_at":"2021-03-04T02:37:39.000Z","dependencies_parsed_at":"2022-08-25T01:41:13.530Z","dependency_job_id":null,"html_url":"https://github.com/prawn-cake/simple-models","commit_stats":null,"previous_names":[],"tags_count":20,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/prawn-cake%2Fsimple-models","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/prawn-cake%2Fsimple-models/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/prawn-cake%2Fsimple-models/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/prawn-cake%2Fsimple-models/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/prawn-cake","download_url":"https://codeload.github.com/prawn-cake/simple-models/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252777950,"owners_count":21802675,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-03T17:00:45.546Z","updated_at":"2025-05-06T22:25:40.916Z","avatar_url":"https://github.com/prawn-cake.png","language":"Python","readme":"# simple-models\n[![Build Status](https://travis-ci.org/prawn-cake/simple-models.svg?branch=master)](https://travis-ci.org/prawn-cake/simple-models)\n[![Documentation Status](https://readthedocs.org/projects/simple-models/badge/?version=latest)](https://readthedocs.org/projects/simple-models/?badge=latest)\n[![Coverage Status](https://coveralls.io/repos/prawn-cake/simple-models/badge.svg?branch=master\u0026service=github)](https://coveralls.io/github/prawn-cake/simple-models?branch=master)\n![PythonVersions](https://www.dropbox.com/s/ck0nc28ttga2pw9/python-2.7_3.4-blue.svg?dl=1)\n\nSimple models is a library which allows you to create validated dictionaries to increase predictability in your application.\n\nUse cases:\n\n* Restrict API messages interactions, bring request and response to predictable data format.\n* Any messages validation, very similar with well-known form features (django forms, wtforms, etc).\n* Work with data flexibly with dict-like structures.\n\n\n## Install\n\n    pip install simple-models\n\n\n## Quick start\n\nDescribe your document model, use suitable fields or nested documents:\n\n    \u003e\u003e\u003e from datetime import datetime\n    \u003e\u003e\u003e from simplemodels.fields import IntegerField, CharField, DocumentField, DateTimeField\n    \u003e\u003e\u003e from simplemodels.models import Document\n    \n    \u003e\u003e\u003e class Address(Document):\n    ...     city = CharField(default='Saint-Petersburg')\n    ...     street = CharField(required=True)\n    \n    \u003e\u003e\u003e class Person(Document):\n    ...  id = IntegerField(default=0)            # supports default values\n    ...  name = CharField(required=True)         # raise exception if not passed\n    ...  address = DocumentField(model=Address)  # nested model validation\n    ...  date_of_birth = DateTimeField(          # date time field with custom format\n    ...             date_fmt='%Y-%m-%d')\n    \n    \u003e\u003e\u003e person = Person({'name': 'John', 'address': {'street': '6th Avenue'}})\n    \u003e\u003e\u003e person\n    Person({'date_of_birth': None, 'id': 0, 'address': Address({'city': u'Saint-Petersburg', 'street': u'6th Avenue'}), 'name': u'John'})\n    \n    \u003e\u003e\u003e person.address\n    Address({'city': u'Saint-Petersburg', 'street': u'6th Avenue'})\n\n    \u003e\u003e\u003e person.address.city\n    u'Saint-Petersburg'\n\n    \u003e\u003e\u003e import json\n    \u003e\u003e\u003e json.dumps(person.as_dict())\n    '{\"date_of_birth\": null, \"id\": 0, \"address\": {\"city\": \"Saint-Petersburg\", \"street\": \"6th Avenue\"}, \"name\": \"John\"}'\n\n## Fields\n* `SimpleField`     -- generic field (useful in cases when other fields are not)\n* `IntegerField`    -- integer field\n* `FloatField`      -- float field\n* `DecimalField`    -- decimal field\n* `CharField`       -- char field (python2/3 portable)\n* `BooleanField`    -- boolean field\n* `DateTimeField`   -- date time field\n* `ListField`       -- list of items field\n* `DocumentField`   -- nested-document field\n* `DictField`       -- dictionary-specific field\n\n\n#### CharField\n\nCharField is a field with default unicode validator (for Python 2), all input strings will be transformed to unicode by default.\n\nExample (for python 2):\n\n    \u003e\u003e\u003e class User(Document):\n    ...  name = CharField()\n        \n    \u003e\u003e\u003e user = User({'name': 'John'})\n    \u003e\u003e\u003e isinstance(user.name, unicode)\n    True\n    \nTo disable this behaviour **(not recommended)**, pass `is_unicode=False` field parameter:\n    \n    \u003e\u003e\u003e class User(Document):\n    ...  name = CharField(is_unicode=False)\n    \n    \u003e\u003e\u003e user = User({'name': 'John'})\n    \u003e\u003e\u003e isinstance(user.name, unicode), isinstance(user.name, str) \n    (False, True)\n\n#### DocumentField\n\nAllows to define nested structures for being validated.\n\nThere are 3 forms to assign a nested model to its' parent:\n\n1. Different models with proper definition order. Keep in mind to define nested model before main one\n   \n \n        class Address(Document):\n            street = CharField()\n    \n        class User(Document):\n            address = DocumentField(model=Address)\n    \n    \n2. Nested modelling - good for keeping \"incapsulation\"\n\n            \n        class User(Document):\n            class _Address(Document):\n                street = CharField()\n            address = DocumentField(model=_Address)\n        \n    \n3. Lazy model assignment with name. Model evaluation happens on validation step, it nicely solves ordering restriction from `#1` \n\n    \n        class User(Document):\n            address = DocumentField(model='Address')\n    \n\n#### ListField\n\nField for mapping to the list of items of a given type. The type of element could be both builtin or custom Model.\nYou can expect the same behaviour as for standard `list` type:\n\nExample:\n\n    \u003e\u003e\u003e from simplemodels.fields import ListField, CharField\n    \u003e\u003e\u003e from simplemodels.models import Document\n    \n    \u003e\u003e\u003e class Comment(Document):\n    ...    body = CharField()\n        \n    \u003e\u003e\u003e class Post(Document):\n    ...    text = CharField()\n    ...    tags = ListField(of=str, default=['news'])\n    ...    comments = ListField(of=Comment)\n    \n    \u003e\u003e\u003e post = Post({'text':\"Do you like cats?\", 'comments':[Comment({'body': \"Yes, they're so cute!\"})]})\n    \u003e\u003e\u003e post.comments.append(dict(body=\"Elephant in the room...\"))\n    \u003e\u003e\u003e post\n    Post({'text': u'Do you like cats?', 'comments': [Comment({'body': u\"Yes, they're so cute!\"}), Comment({'body': u'Elephant in the room...'})], 'tags': ['news']})\n\n\n**NOTE:** mutable default values are protected (deep copied) and works as expected.\n\n**NOTE:** `ListField` always has `default=[]` value\n\n#### DictField\n\nThis type of field enables to be more specific, rather than just using `SimpleField` and also allows to use custom dict implementation, default is `dict`.\n\nExample:\n\n    \u003e\u003e\u003e from simplemodels.fields import DictField\n    \u003e\u003e\u003e from simplemodels.models import Document\n    \u003e\u003e\u003e from collections import OrderedDict\n    \n    \u003e\u003e\u003e class UserAsDict(Document):\n    ...    attrs = DictField(required=True, dict_cls=OrderedDict)\n        \n    \u003e\u003e\u003e UserAsDict({'attrs': [('b', 1), ('a', 2)]}).as_dict()\n    {'attrs': OrderedDict([('b', 1), ('a', 2)])}\n    \n\n\n### Meta\n\n*Meta* is a nested structure to define some extra document options.\n\nExample:\n\n    \u003e\u003e\u003e class UserWithMeta(Document):\n    ...    name = CharField()\n    ...    role = CharField()\n    ...\n    ...    class Meta:\n    ...        ALLOW_EXTRA_FIELDS = True\n    ...        OMIT_MISSED_FIELDS = True\n            \n#### Meta options\n\n* `ALLOW_EXTRA_FIELDS` - accept to put extra fields not defined with schema\n    \n        \u003e\u003e\u003e user = UserWithMeta(dict(name='Maksim', role='Admin', id=47))\n        \u003e\u003e\u003e user\n        UserWithMeta({'role': u'Admin', 'name': u'Maksim', 'id': 47})\n        \n\n* `OMIT_MISSED_FIELDS` - this option lets us omit values with `None`:\n        \n        user = User({'name': 'Maksim'})\n        user\n        \n        # Without option\n        {'name': 'Maksim', 'role': None}\n        \n        # With option\n        {'name': 'Maksim'}\n\n## Validators\n\nValidator is always a callable object which gets data as an argument and validates it. Validator must return `True`, otherwise it's considered failed.\n\nExample of validators: `lambda v: v \u003e 10`, `lambda v: 10 \u003c len(v) \u003c 100`, etc.\n\nValidators can be used as a chain for the field, e.g\n    \n    import hashlib\n    \n    class User(Document):\n        username = CharField()\n        password = CharField(validators=[str, lambda x: hashlib.sha256(x).hexdigest()])\n\n\n### Post-init model validation\n\nHelps to validate your fields when it depends on the other fields\n\nFor example let's validate length of admin password if the user is.\n\n    \u003e\u003e\u003e from simplemodels.fields import CharField, BooleanField\n    \u003e\u003e\u003e from simplemodels.models import Document\n    \u003e\u003e\u003e from simplemodels.exceptions import ModelValidationError\n    \n    \u003e\u003e\u003e class UserWithPassword(Document):\n    ...    name = CharField()\n    ...    password = CharField(required=True)\n    ...    is_admin = BooleanField(default=False)\n    ...\n    ...    @staticmethod\n    ...    def validate_password(document, value):\n    ...        if document.is_admin and len(value) \u003c 10:\n    ...            raise ModelValidationError(\n    ...                'Admin password is too short (\u003c 10 characters)')\n    ...        return value\n    \n    \u003e\u003e\u003e UserWithPassword(dict(name='Normal user', password='foo', is_admin=False))\n    UserWithPassword({'password': u'foo', 'is_admin': False, 'name': u'Normal user'})\n    \u003e\u003e\u003e UserWithPassword(dict(name='Admin user', password='foo', is_admin=True))\n    Traceback (most recent call last):\n      ...\n    ModelValidationError: Admin password is too short (\u003c 10 characters)\n    \n            \n**NOTE:** validation method must be static, have `validate_{field_name}` format and get 2 parameters: *document* and *value*             \n\n\n### Inheritance\n\n`Document` model supports inheritance. \nSometimes it turns out very handy to define base message class and define subclasses inherited from the base one:\n\n    class BaseMessage(Document):\n        method_name = CharField(required=True)    \n        params = DictField(required=True)\n        \n        \n    class HttpRpcMessage(BaseMessage):\n        url = CharField(required=True)\n    \n    \n    class RabbitRpcMessage(BaseMessage):\n        amqp_headers = DictField(required=True)\n    \n\n### Immutable documents and fields\n\nIf you need to make your field or whole document immutable\n\n#### Immutable field\n    \n    \u003e\u003e\u003e from simplemodels.models import Document\n    \n    \u003e\u003e\u003e class UserWithImmutableId(Document):\n    ...    id = IntegerField(immutable=True)\n    ...    name = CharField()\n        \n    \u003e\u003e\u003e user = UserWithImmutableId({'name': 'John', 'id': 1})\n    \u003e\u003e\u003e user.name = 'Mark'\n    \u003e\u003e\u003e user\n    UserWithImmutableId({'id': 1, 'name': u'Mark'})\n    \u003e\u003e\u003e user.id = 2\n    Traceback (most recent call last):\n      ...\n    ImmutableFieldError: UserWithImmutableId.id field is immutable\n    \n#### Immutable document \n\n    \u003e\u003e\u003e from simplemodels.fields import CharField, IntegerField\n    \u003e\u003e\u003e from simplemodels.models import ImmutableDocument\n    \n    \u003e\u003e\u003e class ImmutableUser(ImmutableDocument):\n    ...    id = IntegerField()\n    ...    name = CharField()\n        \n    \u003e\u003e\u003e user = ImmutableUser({'name': 'John', 'id': 1})\n    \u003e\u003e\u003e user.id = 2\n    Traceback (most recent call last):\n      ...\n    DocumentError: ImmutableUser({'id': 1, 'name': u'John'}) is immutable. Set operation is not allowed.\n\n## Run tests\n\n    tox\n\n**NOTE:** In some cases it requires to downgrade your *virtualenv* to *12.0.2* to run it with python 3. \n\nRelated issues: \n\n* [python-future issue](https://github.com/PythonCharmers/python-future/issues/148)\n* [import error issue](http://stackoverflow.com/questions/32861935/passing-python3-to-virtualenvwrapper-throws-up-importerror)\n\n\n\n## Bug tracker\n\nWarm welcome to suggestions and concerns\n\nhttps://github.com/prawn-cake/simple-models/issues\n\n## Contributors (without any specific order)\n- [grundic](https://github.com/grundic)\n\n## License\n\nMIT - http://opensource.org/licenses/MIT\n","funding_links":[],"categories":["Model, Schema"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fprawn-cake%2Fsimple-models","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fprawn-cake%2Fsimple-models","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fprawn-cake%2Fsimple-models/lists"}