pydantic-resolve


Namepydantic-resolve JSON
Version 1.11.1 PyPI version JSON
download
home_pagehttps://github.com/allmonday/pydantic_resolve
SummaryIt just provide a pair of pre & post methods around pydantic fields, the rest is up to your imagination
upload_time2024-11-21 01:14:44
maintainerNone
docs_urlNone
authortangkikodo
requires_python<4.0,>=3.7
licenseMIT
keywords pydantic fastapi
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage
            [![pypi](https://img.shields.io/pypi/v/pydantic-resolve.svg)](https://pypi.python.org/pypi/pydantic-resolve)
[![Downloads](https://static.pepy.tech/personalized-badge/pydantic-resolve?period=month&units=abbreviation&left_color=grey&right_color=orange&left_text=Downloads)](https://pepy.tech/project/pydantic-resolve)
![Python Versions](https://img.shields.io/pypi/pyversions/pydantic-resolve)
[![CI](https://github.com/allmonday/pydantic_resolve/actions/workflows/ci.yml/badge.svg)](https://github.com/allmonday/pydantic_resolve/actions/workflows/ci.yml)

Pydantic-resolve is a schema based solution for data composition, it can provide you with 3 ~ 5 times the increase in development efficiency and reduce the amount of code by more than 50%.

1. It manages the deep data inside each schema, instead of visiting from outside by manual traversal.
2. It runs a Level Order Traversal (BFS) inside and execute `resolve` and `post` during this process.
3. It describes the relationship between data in a form close to ERD (entity relationship diagram)

## Install

~~User of pydantic v2, please use [pydantic2-resolve](https://github.com/allmonday/pydantic2-resolve) instead.~~

This lib now supports both pydantic v1 and v2 starts from v1.11.0

```shell
pip install pydantic-resolve
```

## Hello world

manage your data inside the schema.

```python
class Tree(BaseModel):
    name: str
    number: int
    description: str = ''
    def resolve_description(self):
        return f"I'm {self.name}, my number is {self.number}"
    children: list['Tree'] = []


tree = dict(
    name='root',
    number=1,
    children=[
        dict(
            name='child1',
            number=2,
            children=[
                dict(
                    name='child1-1',
                    number=3,
                ),
                dict(
                    name='child1-2',
                    number=4,
                ),
            ]
        )
    ]
)

async def main():
    t = Tree.parse_obj(tree)
    t = await Resolver().resolve(t)
    print(t.json(indent=4))

import asyncio
asyncio.run(main())
```

output

```json
{
  "name": "root",
  "number": 1,
  "description": "I'm root, my number is 1",
  "children": [
    {
      "name": "child1",
      "number": 2,
      "description": "I'm child1, my number is 2",
      "children": [
        {
          "name": "child1-1",
          "number": 3,
          "description": "I'm child1-1, my number is 3",
          "children": []
        },
        {
          "name": "child1-2",
          "number": 4,
          "description": "I'm child1-2, my number is 4",
          "children": []
        }
      ]
    }
  ]
}
```

## Composing a subset from ERD definitions

![](./doc/imgs/concept.png)

define elements of ERD, schema (entity), dataloader (relationship).

then pick and compose them together according to your requirement and get the result.

```python
import asyncio
import json
from typing import Optional
from pydantic import BaseModel
from pydantic_resolve import Resolver, build_object, build_list, LoaderDepend
from aiodataloader import DataLoader

# Schema/ Entity
class Comment(BaseModel):
    id: int
    content: str
    user_id: int

class Blog(BaseModel):
    id: int
    title: str
    content: str

class User(BaseModel):
    id: int
    name: str


# Loaders/ relationships
class CommentLoader(DataLoader):
    async def batch_load_fn(self, comment_ids):
        comments = [
            dict(id=1, content="world is beautiful", blog_id=1, user_id=1),
            dict(id=2, content="Mars is beautiful", blog_id=2, user_id=2),
            dict(id=3, content="I love Mars", blog_id=2, user_id=3),
        ]
        return build_list(comments, comment_ids, lambda c: c['blog_id'])

class UserLoader(DataLoader):
    async def batch_load_fn(self, user_ids):
        users = [ dict(id=1, name="Alice"), dict(id=2, name="Bob"), ]
        return build_object(users, user_ids, lambda u: u['id'])


# Compose schemas and dataloaders together
class CommentWithUser(Comment):
    user: Optional[User] = None
    def resolve_user(self, loader=LoaderDepend(UserLoader)):
        return loader.load(self.user_id)

class BlogWithComments(Blog):
    comments: list[CommentWithUser] = []
    def resolve_comments(self, loader=LoaderDepend(CommentLoader)):
        return loader.load(self.id)


# Run
async def main():
    raw_blogs =[
        dict(id=1, title="hello world", content="hello world detail"),
        dict(id=2, title="hello Mars", content="hello Mars detail"),
    ]
    blogs = await Resolver().resolve([BlogWithComments.parse_obj(b) for b in raw_blogs])
    print(json.dumps(blogs, indent=2, default=lambda o: o.dict()))

asyncio.run(main())
```

output

```json
[
  {
    "id": 1,
    "title": "hello world",
    "content": "hello world detail",
    "comments": [
      {
        "id": 1,
        "content": "world is beautiful",
        "user_id": 1,
        "user": {
          "id": 1,
          "name": "Alice"
        }
      }
    ]
  },
  {
    "id": 2,
    "title": "hello Mars",
    "content": "hello Mars detail",
    "comments": [
      {
        "id": 2,
        "content": "Mars is beautiful",
        "user_id": 2,
        "user": {
          "id": 2,
          "name": "Bob"
        }
      },
      {
        "id": 3,
        "content": "I love Mars",
        "user_id": 3,
        "user": null
      }
    ]
  }
]
```

## Documents

- **Quick start**: https://allmonday.github.io/pydantic-resolve/about/
- **API**: https://allmonday.github.io/pydantic-resolve/reference_api/
- **Demo**: https://github.com/allmonday/pydantic-resolve-demo
- **Composition oriented pattern**: https://github.com/allmonday/composition-oriented-development-pattern

## Test and coverage

```shell
tox
```

```shell
tox -e coverage
python -m http.server
```

latest coverage: 98%

## Sponsor

If this code helps and you wish to support me

Paypal: https://www.paypal.me/tangkikodo

## Discussion

[Discord](https://discord.com/channels/1197929379951558797/1197929379951558800)

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/allmonday/pydantic_resolve",
    "name": "pydantic-resolve",
    "maintainer": null,
    "docs_url": null,
    "requires_python": "<4.0,>=3.7",
    "maintainer_email": null,
    "keywords": "pydantic, fastapi",
    "author": "tangkikodo",
    "author_email": "allmonday@126.com",
    "download_url": "https://files.pythonhosted.org/packages/38/35/1df7bd8396773075f98cb7ab5a4726df813c37460cd813fb46f5722d9141/pydantic_resolve-1.11.1.tar.gz",
    "platform": null,
    "description": "[![pypi](https://img.shields.io/pypi/v/pydantic-resolve.svg)](https://pypi.python.org/pypi/pydantic-resolve)\n[![Downloads](https://static.pepy.tech/personalized-badge/pydantic-resolve?period=month&units=abbreviation&left_color=grey&right_color=orange&left_text=Downloads)](https://pepy.tech/project/pydantic-resolve)\n![Python Versions](https://img.shields.io/pypi/pyversions/pydantic-resolve)\n[![CI](https://github.com/allmonday/pydantic_resolve/actions/workflows/ci.yml/badge.svg)](https://github.com/allmonday/pydantic_resolve/actions/workflows/ci.yml)\n\nPydantic-resolve is a schema based solution for data composition, it can provide you with 3 ~ 5 times the increase in development efficiency and reduce the amount of code by more than 50%.\n\n1. It manages the deep data inside each schema, instead of visiting from outside by manual traversal.\n2. It runs a Level Order Traversal (BFS) inside and execute `resolve` and `post` during this process.\n3. It describes the relationship between data in a form close to ERD (entity relationship diagram)\n\n## Install\n\n~~User of pydantic v2, please use [pydantic2-resolve](https://github.com/allmonday/pydantic2-resolve) instead.~~\n\nThis lib now supports both pydantic v1 and v2 starts from v1.11.0\n\n```shell\npip install pydantic-resolve\n```\n\n## Hello world\n\nmanage your data inside the schema.\n\n```python\nclass Tree(BaseModel):\n    name: str\n    number: int\n    description: str = ''\n    def resolve_description(self):\n        return f\"I'm {self.name}, my number is {self.number}\"\n    children: list['Tree'] = []\n\n\ntree = dict(\n    name='root',\n    number=1,\n    children=[\n        dict(\n            name='child1',\n            number=2,\n            children=[\n                dict(\n                    name='child1-1',\n                    number=3,\n                ),\n                dict(\n                    name='child1-2',\n                    number=4,\n                ),\n            ]\n        )\n    ]\n)\n\nasync def main():\n    t = Tree.parse_obj(tree)\n    t = await Resolver().resolve(t)\n    print(t.json(indent=4))\n\nimport asyncio\nasyncio.run(main())\n```\n\noutput\n\n```json\n{\n  \"name\": \"root\",\n  \"number\": 1,\n  \"description\": \"I'm root, my number is 1\",\n  \"children\": [\n    {\n      \"name\": \"child1\",\n      \"number\": 2,\n      \"description\": \"I'm child1, my number is 2\",\n      \"children\": [\n        {\n          \"name\": \"child1-1\",\n          \"number\": 3,\n          \"description\": \"I'm child1-1, my number is 3\",\n          \"children\": []\n        },\n        {\n          \"name\": \"child1-2\",\n          \"number\": 4,\n          \"description\": \"I'm child1-2, my number is 4\",\n          \"children\": []\n        }\n      ]\n    }\n  ]\n}\n```\n\n## Composing a subset from ERD definitions\n\n![](./doc/imgs/concept.png)\n\ndefine elements of ERD, schema (entity), dataloader (relationship).\n\nthen pick and compose them together according to your requirement and get the result.\n\n```python\nimport asyncio\nimport json\nfrom typing import Optional\nfrom pydantic import BaseModel\nfrom pydantic_resolve import Resolver, build_object, build_list, LoaderDepend\nfrom aiodataloader import DataLoader\n\n# Schema/ Entity\nclass Comment(BaseModel):\n    id: int\n    content: str\n    user_id: int\n\nclass Blog(BaseModel):\n    id: int\n    title: str\n    content: str\n\nclass User(BaseModel):\n    id: int\n    name: str\n\n\n# Loaders/ relationships\nclass CommentLoader(DataLoader):\n    async def batch_load_fn(self, comment_ids):\n        comments = [\n            dict(id=1, content=\"world is beautiful\", blog_id=1, user_id=1),\n            dict(id=2, content=\"Mars is beautiful\", blog_id=2, user_id=2),\n            dict(id=3, content=\"I love Mars\", blog_id=2, user_id=3),\n        ]\n        return build_list(comments, comment_ids, lambda c: c['blog_id'])\n\nclass UserLoader(DataLoader):\n    async def batch_load_fn(self, user_ids):\n        users = [ dict(id=1, name=\"Alice\"), dict(id=2, name=\"Bob\"), ]\n        return build_object(users, user_ids, lambda u: u['id'])\n\n\n# Compose schemas and dataloaders together\nclass CommentWithUser(Comment):\n    user: Optional[User] = None\n    def resolve_user(self, loader=LoaderDepend(UserLoader)):\n        return loader.load(self.user_id)\n\nclass BlogWithComments(Blog):\n    comments: list[CommentWithUser] = []\n    def resolve_comments(self, loader=LoaderDepend(CommentLoader)):\n        return loader.load(self.id)\n\n\n# Run\nasync def main():\n    raw_blogs =[\n        dict(id=1, title=\"hello world\", content=\"hello world detail\"),\n        dict(id=2, title=\"hello Mars\", content=\"hello Mars detail\"),\n    ]\n    blogs = await Resolver().resolve([BlogWithComments.parse_obj(b) for b in raw_blogs])\n    print(json.dumps(blogs, indent=2, default=lambda o: o.dict()))\n\nasyncio.run(main())\n```\n\noutput\n\n```json\n[\n  {\n    \"id\": 1,\n    \"title\": \"hello world\",\n    \"content\": \"hello world detail\",\n    \"comments\": [\n      {\n        \"id\": 1,\n        \"content\": \"world is beautiful\",\n        \"user_id\": 1,\n        \"user\": {\n          \"id\": 1,\n          \"name\": \"Alice\"\n        }\n      }\n    ]\n  },\n  {\n    \"id\": 2,\n    \"title\": \"hello Mars\",\n    \"content\": \"hello Mars detail\",\n    \"comments\": [\n      {\n        \"id\": 2,\n        \"content\": \"Mars is beautiful\",\n        \"user_id\": 2,\n        \"user\": {\n          \"id\": 2,\n          \"name\": \"Bob\"\n        }\n      },\n      {\n        \"id\": 3,\n        \"content\": \"I love Mars\",\n        \"user_id\": 3,\n        \"user\": null\n      }\n    ]\n  }\n]\n```\n\n## Documents\n\n- **Quick start**: https://allmonday.github.io/pydantic-resolve/about/\n- **API**: https://allmonday.github.io/pydantic-resolve/reference_api/\n- **Demo**: https://github.com/allmonday/pydantic-resolve-demo\n- **Composition oriented pattern**: https://github.com/allmonday/composition-oriented-development-pattern\n\n## Test and coverage\n\n```shell\ntox\n```\n\n```shell\ntox -e coverage\npython -m http.server\n```\n\nlatest coverage: 98%\n\n## Sponsor\n\nIf this code helps and you wish to support me\n\nPaypal: https://www.paypal.me/tangkikodo\n\n## Discussion\n\n[Discord](https://discord.com/channels/1197929379951558797/1197929379951558800)\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "It just provide a pair of pre & post methods around pydantic fields, the rest is up to your imagination",
    "version": "1.11.1",
    "project_urls": {
        "Homepage": "https://github.com/allmonday/pydantic_resolve",
        "Repository": "https://github.com/allmonday/pydantic_resolve"
    },
    "split_keywords": [
        "pydantic",
        " fastapi"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "dbbc65c15bac87778da997e94abf2e6ae3a993501a8322f7f1f9a372adddde9c",
                "md5": "bdce35f567bb98b42a2efc9cc5008ba4",
                "sha256": "ff00d4620fe72b0febbd2e0f1b6e446b4fd0761d54e6fb4aa0672b3795b9bceb"
            },
            "downloads": -1,
            "filename": "pydantic_resolve-1.11.1-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "bdce35f567bb98b42a2efc9cc5008ba4",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": "<4.0,>=3.7",
            "size": 20643,
            "upload_time": "2024-11-21T01:14:43",
            "upload_time_iso_8601": "2024-11-21T01:14:43.358186Z",
            "url": "https://files.pythonhosted.org/packages/db/bc/65c15bac87778da997e94abf2e6ae3a993501a8322f7f1f9a372adddde9c/pydantic_resolve-1.11.1-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "38351df7bd8396773075f98cb7ab5a4726df813c37460cd813fb46f5722d9141",
                "md5": "f6e3c99870f7b3897b1c02c97232012a",
                "sha256": "9819e60e272cd63200e8ede9350d04ba6d2d52a38a573d78b650cc50891f81b9"
            },
            "downloads": -1,
            "filename": "pydantic_resolve-1.11.1.tar.gz",
            "has_sig": false,
            "md5_digest": "f6e3c99870f7b3897b1c02c97232012a",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": "<4.0,>=3.7",
            "size": 17644,
            "upload_time": "2024-11-21T01:14:44",
            "upload_time_iso_8601": "2024-11-21T01:14:44.819434Z",
            "url": "https://files.pythonhosted.org/packages/38/35/1df7bd8396773075f98cb7ab5a4726df813c37460cd813fb46f5722d9141/pydantic_resolve-1.11.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-11-21 01:14:44",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "allmonday",
    "github_project": "pydantic_resolve",
    "travis_ci": false,
    "coveralls": true,
    "github_actions": true,
    "tox": true,
    "lcname": "pydantic-resolve"
}
        
Elapsed time: 0.35363s