pydantic-view


Namepydantic-view JSON
Version 0.3.0 PyPI version JSON
download
home_page
SummaryView decorator to create the child pydantic models from the root model.
upload_time2023-08-18 17:40:56
maintainer
docs_urlNone
authorRoman Koshel
requires_python>=3.8,<4.0
licenseMIT
keywords pydantic model view utils
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # Pydantic view helper decorator

[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

### Installation
```bash
pip install pydantic_view
```

### Usage

```python
In [1]: from uuid import UUID, uuid4
   ...: 
   ...: from pydantic import BaseModel, Field
   ...: from pydantic_view import view
   ...: 
   ...: 
   ...: @view("Create", exclude={"id"})
   ...: @view("Update")
   ...: @view("Patch", optional={"username", "password", "address"})
   ...: @view("Out", exclude={"password"})
   ...: class User(BaseModel):
   ...:     id: int
   ...:     username: str
   ...:     password: str
   ...:     address: str
   ...: 

In [2]: user = User(id=0, username="human", password="iamaman", address="Earth")
   ...: user.Out()
   ...: 
Out[2]: UserOut(id=0, username='human', address='Earth')

In [3]: User.Update(id=0, username="human", password="iamasuperman", address="Earth")
   ...: 
Out[3]: UserUpdate(id=0, username='human', password='iamasuperman', address='Earth')

In [4]: User.Patch(id=0, address="Mars")
   ...: 
Out[4]: UserPatch(id=0, username=None, password=None, address='Mars')
```


### FastAPI example

```python
from typing import List

from fastapi import FastAPI
from fastapi.testclient import TestClient
from pydantic import BaseModel, Field

from pydantic_view import view, view_validator


@view("Out", exclude={"secret"})
@view("Create", exclude={"id"}, config={"extra": "forbid"})
@view("Update", exclude={"id"})
@view("UpdateMany")
@view("Patch", exclude={"id"}, optional={"name", "secret"})
@view("PatchMany", optional={"name", "secret"})
class Group(BaseModel):
    id: int
    name: str
    secret: str = None

    @view_validator(["Create", "Update", "UpdateMany", "Patch", "PatchMany"], "name", allow_reuse=True)
    def validate_name(cls, v):
        if v == "admin":
            raise ValueError("Invalid name")
        return v


@view("Out", exclude={"password"}, recursive=True)
@view(
    "Create",
    exclude={"id"},
    fields={"groups": Field(default_factory=lambda: [Group(id=0, name="default")])},
    config={"extra": "forbid"},
    recursive=True,
)
@view("Update", exclude={"id"}, recursive=True)
@view("UpdateMany", recursive=True)
@view("Patch", exclude={"id"}, optional={"username", "password", "groups"}, recursive=True)
@view("PatchMany", optional={"username", "password", "groups"}, recursive=True)
class User(BaseModel):
    id: int
    username: str
    password: str
    groups: List[Group]


app = FastAPI()

db = {}


@app.get("/users/{user_id}", response_model=User.Out)
async def get(user_id: int) -> User.Out:
    return db[user_id]


@app.post("/users", response_model=User.Out)
async def post(user: User.Create) -> User.Out:
    user_id = 0  # generate_user_id()
    db[0] = User(id=user_id, **user.dict())
    return db[0]


@app.put("/users/{user_id}", response_model=User.Out)
async def put(user_id: int, user: User.Update) -> User.Out:
    db[user_id] = User(id=user_id, **user.dict())
    return db[user_id]


@app.put("/users", response_model=List[User.Out])
async def put_many(users: List[User.UpdateMany]) -> List[User.Out]:
    for user in users:
        db[user.id] = user
    return users


@app.patch("/users/{user_id}", response_model=User.Out)
async def patch(user_id: int, user: User.Patch) -> User.Out:
    db[user_id] = User(**{**db[user_id].dict(), **user.dict(exclude_unset=True)})
    return db[user_id]


@app.patch("/users", response_model=List[User.Out])
async def patch_many(users: List[User.PatchMany]) -> List[User.Out]:
    for user in users:
        db[user.id] = User(**{**db[user.id].dict(), **user.dict(exclude_unset=True)})
    return [db[user.id] for user in users]


def test_fastapi():
    client = TestClient(app)

    # POST
    response = client.post(
        "/users",
        json={
            "username": "admin",
            "password": "admin",
        },
    )
    assert response.status_code == 200
    assert response.json() == {
        "id": 0,
        "username": "admin",
        "groups": [{"id": 0, "name": "default"}],
    }

    # GET
    response = client.get("/users/0")
    assert response.status_code == 200
    assert response.json() == {
        "id": 0,
        "username": "admin",
        "groups": [{"id": 0, "name": "default"}],
    }

    # PUT
    response = client.put(
        "/users/0",
        json={
            "username": "superadmin",
            "password": "superadmin",
            "groups": [],
        },
    )
    assert response.status_code == 200
    assert response.json() == {
        "id": 0,
        "username": "superadmin",
        "groups": [],
    }

    # PUT many
    response = client.put(
        "/users",
        json=[
            {
                "id": 0,
                "username": "admin",
                "password": "admin",
                "groups": [{"id": 0, "name": "default", "secret": "secret_value"}],
            }
        ],
    )
    assert response.status_code == 200
    assert response.json() == [
        {
            "id": 0,
            "username": "admin",
            "groups": [{"id": 0, "name": "default"}],
        }
    ]

    # PATCH
    response = client.patch("/users/0", json={"id": 0, "username": "guest"})
    assert response.status_code == 200
    assert response.json() == {
        "id": 0,
        "username": "guest",
        "groups": [{"id": 0, "name": "default"}],
    }

    # PATCH many
    response = client.patch("/users", json=[{"id": 0, "groups": []}])
    assert response.status_code == 200
    assert response.json() == [
        {
            "id": 0,
            "username": "guest",
            "groups": [],
        }
    ]
```

            

Raw data

            {
    "_id": null,
    "home_page": "",
    "name": "pydantic-view",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.8,<4.0",
    "maintainer_email": "",
    "keywords": "pydantic,model,view,utils",
    "author": "Roman Koshel",
    "author_email": "roma.koshel@gmail.com",
    "download_url": "https://files.pythonhosted.org/packages/2a/4d/39beca10ba8255f102d03a4f2dc410e3e09a08fd1410e84796c9e91ea75e/pydantic_view-0.3.0.tar.gz",
    "platform": null,
    "description": "# Pydantic view helper decorator\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\n### Installation\n```bash\npip install pydantic_view\n```\n\n### Usage\n\n```python\nIn [1]: from uuid import UUID, uuid4\n   ...: \n   ...: from pydantic import BaseModel, Field\n   ...: from pydantic_view import view\n   ...: \n   ...: \n   ...: @view(\"Create\", exclude={\"id\"})\n   ...: @view(\"Update\")\n   ...: @view(\"Patch\", optional={\"username\", \"password\", \"address\"})\n   ...: @view(\"Out\", exclude={\"password\"})\n   ...: class User(BaseModel):\n   ...:     id: int\n   ...:     username: str\n   ...:     password: str\n   ...:     address: str\n   ...: \n\nIn [2]: user = User(id=0, username=\"human\", password=\"iamaman\", address=\"Earth\")\n   ...: user.Out()\n   ...: \nOut[2]: UserOut(id=0, username='human', address='Earth')\n\nIn [3]: User.Update(id=0, username=\"human\", password=\"iamasuperman\", address=\"Earth\")\n   ...: \nOut[3]: UserUpdate(id=0, username='human', password='iamasuperman', address='Earth')\n\nIn [4]: User.Patch(id=0, address=\"Mars\")\n   ...: \nOut[4]: UserPatch(id=0, username=None, password=None, address='Mars')\n```\n\n\n### FastAPI example\n\n```python\nfrom typing import List\n\nfrom fastapi import FastAPI\nfrom fastapi.testclient import TestClient\nfrom pydantic import BaseModel, Field\n\nfrom pydantic_view import view, view_validator\n\n\n@view(\"Out\", exclude={\"secret\"})\n@view(\"Create\", exclude={\"id\"}, config={\"extra\": \"forbid\"})\n@view(\"Update\", exclude={\"id\"})\n@view(\"UpdateMany\")\n@view(\"Patch\", exclude={\"id\"}, optional={\"name\", \"secret\"})\n@view(\"PatchMany\", optional={\"name\", \"secret\"})\nclass Group(BaseModel):\n    id: int\n    name: str\n    secret: str = None\n\n    @view_validator([\"Create\", \"Update\", \"UpdateMany\", \"Patch\", \"PatchMany\"], \"name\", allow_reuse=True)\n    def validate_name(cls, v):\n        if v == \"admin\":\n            raise ValueError(\"Invalid name\")\n        return v\n\n\n@view(\"Out\", exclude={\"password\"}, recursive=True)\n@view(\n    \"Create\",\n    exclude={\"id\"},\n    fields={\"groups\": Field(default_factory=lambda: [Group(id=0, name=\"default\")])},\n    config={\"extra\": \"forbid\"},\n    recursive=True,\n)\n@view(\"Update\", exclude={\"id\"}, recursive=True)\n@view(\"UpdateMany\", recursive=True)\n@view(\"Patch\", exclude={\"id\"}, optional={\"username\", \"password\", \"groups\"}, recursive=True)\n@view(\"PatchMany\", optional={\"username\", \"password\", \"groups\"}, recursive=True)\nclass User(BaseModel):\n    id: int\n    username: str\n    password: str\n    groups: List[Group]\n\n\napp = FastAPI()\n\ndb = {}\n\n\n@app.get(\"/users/{user_id}\", response_model=User.Out)\nasync def get(user_id: int) -> User.Out:\n    return db[user_id]\n\n\n@app.post(\"/users\", response_model=User.Out)\nasync def post(user: User.Create) -> User.Out:\n    user_id = 0  # generate_user_id()\n    db[0] = User(id=user_id, **user.dict())\n    return db[0]\n\n\n@app.put(\"/users/{user_id}\", response_model=User.Out)\nasync def put(user_id: int, user: User.Update) -> User.Out:\n    db[user_id] = User(id=user_id, **user.dict())\n    return db[user_id]\n\n\n@app.put(\"/users\", response_model=List[User.Out])\nasync def put_many(users: List[User.UpdateMany]) -> List[User.Out]:\n    for user in users:\n        db[user.id] = user\n    return users\n\n\n@app.patch(\"/users/{user_id}\", response_model=User.Out)\nasync def patch(user_id: int, user: User.Patch) -> User.Out:\n    db[user_id] = User(**{**db[user_id].dict(), **user.dict(exclude_unset=True)})\n    return db[user_id]\n\n\n@app.patch(\"/users\", response_model=List[User.Out])\nasync def patch_many(users: List[User.PatchMany]) -> List[User.Out]:\n    for user in users:\n        db[user.id] = User(**{**db[user.id].dict(), **user.dict(exclude_unset=True)})\n    return [db[user.id] for user in users]\n\n\ndef test_fastapi():\n    client = TestClient(app)\n\n    # POST\n    response = client.post(\n        \"/users\",\n        json={\n            \"username\": \"admin\",\n            \"password\": \"admin\",\n        },\n    )\n    assert response.status_code == 200\n    assert response.json() == {\n        \"id\": 0,\n        \"username\": \"admin\",\n        \"groups\": [{\"id\": 0, \"name\": \"default\"}],\n    }\n\n    # GET\n    response = client.get(\"/users/0\")\n    assert response.status_code == 200\n    assert response.json() == {\n        \"id\": 0,\n        \"username\": \"admin\",\n        \"groups\": [{\"id\": 0, \"name\": \"default\"}],\n    }\n\n    # PUT\n    response = client.put(\n        \"/users/0\",\n        json={\n            \"username\": \"superadmin\",\n            \"password\": \"superadmin\",\n            \"groups\": [],\n        },\n    )\n    assert response.status_code == 200\n    assert response.json() == {\n        \"id\": 0,\n        \"username\": \"superadmin\",\n        \"groups\": [],\n    }\n\n    # PUT many\n    response = client.put(\n        \"/users\",\n        json=[\n            {\n                \"id\": 0,\n                \"username\": \"admin\",\n                \"password\": \"admin\",\n                \"groups\": [{\"id\": 0, \"name\": \"default\", \"secret\": \"secret_value\"}],\n            }\n        ],\n    )\n    assert response.status_code == 200\n    assert response.json() == [\n        {\n            \"id\": 0,\n            \"username\": \"admin\",\n            \"groups\": [{\"id\": 0, \"name\": \"default\"}],\n        }\n    ]\n\n    # PATCH\n    response = client.patch(\"/users/0\", json={\"id\": 0, \"username\": \"guest\"})\n    assert response.status_code == 200\n    assert response.json() == {\n        \"id\": 0,\n        \"username\": \"guest\",\n        \"groups\": [{\"id\": 0, \"name\": \"default\"}],\n    }\n\n    # PATCH many\n    response = client.patch(\"/users\", json=[{\"id\": 0, \"groups\": []}])\n    assert response.status_code == 200\n    assert response.json() == [\n        {\n            \"id\": 0,\n            \"username\": \"guest\",\n            \"groups\": [],\n        }\n    ]\n```\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "View decorator to create the child pydantic models from the root model.",
    "version": "0.3.0",
    "project_urls": null,
    "split_keywords": [
        "pydantic",
        "model",
        "view",
        "utils"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "c597c5ed38fb4f506ef7991a594b889ec50bed8fc87ecf93dbe56a2688091bcf",
                "md5": "4c8086467075e48ac95e5f936630b71c",
                "sha256": "b275b6bfdc101892300231256f14fddae2a74004c4a257dc04f09fec2c8285e2"
            },
            "downloads": -1,
            "filename": "pydantic_view-0.3.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "4c8086467075e48ac95e5f936630b71c",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8,<4.0",
            "size": 5874,
            "upload_time": "2023-08-18T17:40:54",
            "upload_time_iso_8601": "2023-08-18T17:40:54.187696Z",
            "url": "https://files.pythonhosted.org/packages/c5/97/c5ed38fb4f506ef7991a594b889ec50bed8fc87ecf93dbe56a2688091bcf/pydantic_view-0.3.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "2a4d39beca10ba8255f102d03a4f2dc410e3e09a08fd1410e84796c9e91ea75e",
                "md5": "58b436d93f1dfc6f9c9fdb85d5637606",
                "sha256": "35daf3998c5181e13ff9a2a1cb7c36fbc2d308736ad3364fb14298394163dca6"
            },
            "downloads": -1,
            "filename": "pydantic_view-0.3.0.tar.gz",
            "has_sig": false,
            "md5_digest": "58b436d93f1dfc6f9c9fdb85d5637606",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8,<4.0",
            "size": 5354,
            "upload_time": "2023-08-18T17:40:56",
            "upload_time_iso_8601": "2023-08-18T17:40:56.175068Z",
            "url": "https://files.pythonhosted.org/packages/2a/4d/39beca10ba8255f102d03a4f2dc410e3e09a08fd1410e84796c9e91ea75e/pydantic_view-0.3.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2023-08-18 17:40:56",
    "github": false,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "lcname": "pydantic-view"
}
        
Elapsed time: 0.22272s