{"id":13936529,"url":"https://github.com/genomoncology/related","last_synced_at":"2025-07-19T22:30:35.684Z","repository":{"id":21316670,"uuid":"92290249","full_name":"genomoncology/related","owner":"genomoncology","description":"Nested Object Models in Python with dictionary, YAML, and JSON transformation support","archived":false,"fork":false,"pushed_at":"2022-12-27T17:03:17.000Z","size":337,"stargazers_count":197,"open_issues_count":22,"forks_count":15,"subscribers_count":15,"default_branch":"master","last_synced_at":"2024-04-26T17:45:09.308Z","etag":null,"topics":["json","object-relational-mapper","python","yaml"],"latest_commit_sha":null,"homepage":null,"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/genomoncology.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":"2017-05-24T12:31:44.000Z","updated_at":"2024-01-17T20:28:53.000Z","dependencies_parsed_at":"2023-01-13T21:24:01.249Z","dependency_job_id":null,"html_url":"https://github.com/genomoncology/related","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/genomoncology%2Frelated","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/genomoncology%2Frelated/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/genomoncology%2Frelated/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/genomoncology%2Frelated/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/genomoncology","download_url":"https://codeload.github.com/genomoncology/related/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":226686728,"owners_count":17666928,"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":["json","object-relational-mapper","python","yaml"],"created_at":"2024-08-07T23:02:45.397Z","updated_at":"2024-11-27T04:31:08.531Z","avatar_url":"https://github.com/genomoncology.png","language":"Python","funding_links":[],"categories":["Python","Projects"],"sub_categories":["YAML is a **supserset of JSON**"],"readme":"\u003cimg src='./.images/logo.png' width='140' align=\"left\" /\u003e\n\u003ca href='https://codecov.io/github/genomoncology/related/'\u003e\u003cimg src='https://codecov.io/github/genomoncology/related/branch/master/graph/badge.svg' align=\"right\" /\u003e\u003c/a\u003e\n\u003ca href='https://travis-ci.org/genomoncology/related'\u003e\u003cimg src='https://img.shields.io/travis/genomoncology/related.svg' align=\"right\" /\u003e\u003c/a\u003e\n\u003ca href='https://pypi.python.org/pypi/related'\u003e\u003cimg src='https://img.shields.io/pypi/v/related.svg' align=\"right\" /\u003e\u003c/a\u003e\n\n\u003cbr/\u003e\u003cbr/\u003e\n\n`Related` is a Python library for creating nested object models\nthat can be serialized to and de-serialized from\nnested python dictionaries.\nWhen paired with other libraries (e.g. [PyYAML]),\n`Related` object models can be used to convert to and from\nnested data formats (e.g. JSON, YAML).\n\nExample use cases for `related` object models include:\n\n* Configuration file reading and writing\n* REST API message response generation and request processing\n* Object-Document Mapping for a document store (e.g. MongoDB, elasticsearch)\n* Data import parsing or export generation\n\n\u003cbr/\u003e\n\n![flow-image]\n\n\u003cbr/\u003e\n\n# Requirements\n\n* Python (2.7, 3.5, 3.6)\n\n\n# Installation\n\nInstall using `pip`...\n\n    pip install related\n\n\n# First Example\n\n```python\nimport related\n\n@related.immutable\nclass Person(object):\n    first_name = related.StringField()\n    last_name = related.StringField()\n\n@related.immutable\nclass RoleModels(object):\n    scientists = related.SetField(Person)\n\npeople = [Person(first_name=\"Grace\", last_name=\"Hopper\"),\n          Person(first_name=\"Katherine\", last_name=\"Johnson\"),\n          Person(first_name=\"Katherine\", last_name=\"Johnson\")]\n\nprint(related.to_yaml(RoleModels(scientists=people)))\n```\n\nYields:\n\n```yaml\nscientists:\n- first_name: Grace\n  last_name: Hopper\n- first_name: Katherine\n  last_name: Johnson\n```\n\n\n# Second Example\n\nThe below example is based off of this [Docker Compose example].\nIt shows how a YAML file can be loaded into an object model, tested, and\nthen generated back into a string that matches the original YAML.\n\n```yaml\nversion: '2'\nservices:\n  web:\n    build: .\n    ports:\n    - 5000:5000\n    volumes:\n    - .:/code\n  redis:\n    image: redis\n```\n\nBelow is the `related` object model that represents the above configuration.\nNotice how the name-based mapping of services (i.e. web, redis) are\nrepresented by the model.\n\n\n```python\nimport related\n\n\n@related.immutable\nclass Service(object):\n    name = related.StringField()\n    image = related.StringField(required=False)\n    build = related.StringField(required=False)\n    ports = related.SequenceField(str, required=False)\n    volumes = related.SequenceField(str, required=False)\n    command = related.StringField(required=False)\n\n\n@related.immutable\nclass Compose(object):\n    version = related.StringField(required=False, default=None)\n    services = related.MappingField(Service, \"name\", required=False)\n```\n\nThe above yaml can then be loaded by using one of the convenience\nmethod and then round-tripped back to yaml to check that the format\nhas been maintained. The `related` module uses `OrderedDict` objects\nin order to maintain sort order by default.\n\n```python\nfrom os.path import join, dirname\n\nfrom model import Compose\nfrom related import to_yaml, from_yaml, to_model\n\nYML_FILE = join(dirname(__file__), \"docker-compose.yml\")\n\n\ndef test_compose_from_yml():\n    original_yaml = open(YML_FILE).read().strip()\n    yml_dict = from_yaml(original_yaml)\n    compose = to_model(Compose, yml_dict)\n\n    assert compose.version == '2'\n    assert compose.services['web'].ports == [\"5000:5000\"]\n    assert compose.services['redis'].image == \"redis\"\n\n    generated_yaml = to_yaml(compose,\n                             suppress_empty_values=True,\n                             suppress_map_key_values=True).strip()\n\n    assert original_yaml == generated_yaml\n```\n\n\n# More Examples\n\nMore examples can be found by reviewing the [tests/] folder of this project.\nBelow are links and descriptions of the tests provided so far.\n\n| Example        | description                                                        |\n| -------------- | ------------------------------------------------------------------ |\n| [Example 00]   | First example above that shows how SetFields work.                 |\n| [Example 01]   | Second example above that demonstrates YAML (de)serialization.     |\n| [Example 02]   | Compose v3 with long-form ports and singledispatch to_dict         |\n| [Example 03]   | A single class (Company) with a bunch of value fields.             |\n| [Example 04]   | A multi-class object model with Enum class value field.            |\n| [Example 05]   | Handling of renaming of attributes including Python keywords.      |\n| [Example 06]   | Basic JSON (de)serialization with TimeField, DateTimeField and DecimalField.     |\n| [Example 07]   | Function decorator that converts inputs to obj and outputs to dict |\n| [Example 08]   | Handle self-referencing and out-of-order references using strings. |\n\n\n# Documentation\n\nBelow is a quick version of documentation until more time can be dedicated.\n\n\n## Overview\n\nThe [attrs] library is the underlying engine for `related`.\nAs explained in [this article by Glyph],\n`attrs` cleanly and cleverly\neliminates a lot of the boilerplate\nrequired when creating Python classes\nwithout using inheritance.\nSome core functionality provided by attrs:\n\n* Generated initializer method\n    (``__init__``)\n* Generated comparison methods\n    (``__eq__``, ``__ne__``, ``__lt__``, ``__le__``, ``__gt__``, ``__ge__`` )\n* Human-readable representation method\n    (``__repr__``)\n* Attribute converter and validator framework\n\n\nThe `related` project is an opinionated layer\nbuilt on top of the `attrs` library\nthat provides the following:\n\n* Value fields that handle both validation and conversion\n  to and from basic data types like\n  ``str``, ``float``, and ``bool``.\n* Nested fields that support relationships such as\n  Child, Sequences, Mappings, and Sets of objects.\n* ``to_dict`` function that converts nested object graphs\n  to python dictionaries.\n  Made customizable (without resorting to [monkey-patching])\n  by the [singledispatch library].\n* ``to_model`` function that instantiated classes\n  used by the de-serialization process going from\n  python dictionaries to the related model.\n* Conversion helper functions\n  (``to_yaml``, ``from_yaml``, ``to_json``, ``from_json``)\n  for easily going between\n  related models and data formats.\n* ``@mutable`` and ``@immutable`` for decorating classes\n  as related models without the need for inheritance increasing\n  maintainability and flexibility.\n\n\n## Class Decorators\n\n| decorator             | description                                                      |\n| --------------        | ---------------------------------------------------------------- |\n| @mutable              | Activate a related class that instantiates changeable objects.   |\n| @immutable            | Activate a related class that instantiates unchangeable objects. |\n\nSee the [decorators.py] file to view the source code until proper\ndocumentation is generated.\n\n\n## Field Types\n\n| field type            | description                                                      |\n| --------------        | ---------------------------------------------------------------- |\n| BooleanField          | `bool` value field.                                              |\n| ChildField            | Child object of a specified type `cls`.                          |\n| DateField             | `date` field formatted using `formatter`.                        |\n| DateTimeField         | `datetime` field formatted using `formatter`.                    |\n| TimeField             | `time` field formatted using `formatter`.                    |\n| FloatField            | `float` value field.                                             |\n| IntegerField          | `int` value field.                                               |\n| MappingField(cls,key) | Dictionary of objects of type `cls` index by `key` field values. |\n| RegexField(regex)     | `str` value field that is validated by re.match(`regex`).        |\n| SequenceField(cls)    | List of objects all of specified type `cls`.                     |\n| SetField              | Set of objects all of a specified type `cls`.                    |\n| StringField           | `str` value field.                                               |\n| URLField              | [ParseResult] object.                                            |\n| UUIDField             | [UUID] object, will create [uuid4] by default if not specified.  |\n\n\nAdding your own field types is fairly straightforward\ndue to the power of the underlying `attrs` project.\nSee the [fields.py] file to see how the above are constructed.\n\n\n## Functions\n\n| function            | description                                           |\n| ------------------- | ----------------------------------------------------- |\n| from_json(s,cls)    | Convert a JSON string or stream into specified class. |\n| from_yaml(s,cls)    | Convert a YAML string or stream into specified class. |\n| is_related(obj)     | Returns True if object is @mutable or @immutable.     |\n| to_dict(obj)        | Singledispatch function for converting to a dict.     |\n| to_json(obj)        | Convert object to a (pretty) JSON string via to_dict. |\n| to_model(cls,value) | Convert a value to a `cls` instance.                  |\n| to_yaml(obj)        | Convert object to a YAML string via to_dict.          |\n\n\nSee the [functions.py] file to view the source code until proper\ndocumentation is generated.\n\n\n# Credits/Prior Art\n\nThe `related` project has been heavily influenced by the following\nprojects that might be worth looking at if `related` doesn't meet your needs.\n\n* [attrs] - The engine that powers `related` functionality.\n* [Django ORM] - Object-relational mapping for Django that inspired `related's` design.\n* [cattrs] - Alternative take for handling nested-objects using `attrs`.\n* [addict] and [box] - Python dictionary wrappers that do not require a model.\n* [Jackson] - Java-based technology for serializing and de-serializing objects.\n\n\n# License\n\nThe MIT License (MIT)\nCopyright (c) 2017 [Ian Maurer], [Genomoncology LLC]\n\n\n\n\n[flow-image]: ./.images/flow.png\n[decorators.py]: ./src/related/decorators.py\n[fields.py]: ./src/related/fields.py\n[functions.py]: ./src/related/functions.py\n[attrs]: http://attrs.readthedocs.io/en/stable/\n[this article by Glyph]: https://glyph.twistedmatrix.com/2016/08/attrs.html\n[Genomoncology LLC]: http://genomoncology.com\n[Ian Maurer]: https://github.com/imaurer\n[singledispatch library]: https://pypi.python.org/pypi/singledispatch\n[monkey-patching]: http://stackoverflow.com/questions/5626193/what-is-a-monkey-patch\n[Django ORM]: https://docs.djangoproject.com/en/1.11/topics/db/models/\n[UUID]: https://docs.python.org/3/library/uuid.html#uuid.UUID\n[uuid4]: https://docs.python.org/3/library/uuid.html#uuid.uuid4\n[ParseResult]: https://docs.python.org/2/library/urlparse.html#urlparse.ParseResult\n[cattrs]: http://cattrs.readthedocs.io/en/latest/readme.html\n[addict]: https://github.com/mewwts/addict\n[box]: https://pypi.python.org/pypi/python-box\n[Jackson]: https://github.com/FasterXML/jackson\n[Docker Compose example]: https://docs.docker.com/compose/gettingstarted/#step-3-define-services-in-a-compose-file\n[PyYAML]: https://pypi.python.org/pypi/PyYAML\n\n[tests/]: ./tests/\n[Example 00]: ./tests/ex00_sets_hashes\n[Example 01]: ./tests/ex01_compose_v2\n[Example 02]: ./tests/ex02_compose_v3.2\n[Example 03]: ./tests/ex03_company\n[Example 04]: ./tests/ex04_contact\n[Example 05]: ./tests/ex05_field_names\n[Example 06]: ./tests/ex06_json\n[Example 07]: ./tests/ex07_serializer\n[Example 08]: ./tests/ex08_self_reference\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgenomoncology%2Frelated","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgenomoncology%2Frelated","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgenomoncology%2Frelated/lists"}