pydantic-view


Namepydantic-view JSON
Version 2.0.1 PyPI version JSON
download
home_pageNone
SummaryView decorator to create the child pydantic models from the root model.
upload_time2024-10-14 14:34:39
maintainerNone
docs_urlNone
authorRoman Koshel
requires_python<4.0,>=3.8
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 pydantic import BaseModel, Field
   ...: from pydantic_view import view
   ...: 
   ...: 
   ...: class User(BaseModel):
   ...:     id: int
   ...:     username: str
   ...:     password: str
   ...:     address: str
   ...: 
   ...: @view("Create", exclude={"id"})
   ...: class UserCreate(User):
   ...:     pass
   ...:
   ...: @view("Update")
   ...: class UserUpdate(User):
   ...:     pass
   ...:
   ...: @view("Patch")
   ...: class UserPatch(User):
   ...:     username: str = None
   ...:     password: str = None
   ...:     address: str = None
   ...:
   ...: @view("Out", exclude={"password"})
   ...: class UserOut(User):
   ...:     pass

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 Optional

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

from pydantic_view import view, view_field_validator


class UserSettings(BaseModel):
    model_config = ConfigDict(extra="forbid")

    public: Optional[str] = None
    secret: Optional[str] = None


@view("Out", exclude={"secret"})
class UserSettingsOut(UserSettings):
    pass


@view("Create")
class UserSettingsCreate(UserSettings):
    pass


@view("Update")
class UserSettingsUpdate(UserSettings):
    pass


@view("Patch")
class UserSettingsPatch(UserSettings):
    public: str = None
    secret: str = None


class User(BaseModel):
    model_config = ConfigDict(extra="forbid")

    id: int
    username: str
    password: str = Field(default_factory=lambda: "password")
    settings: UserSettings

    @view_field_validator({"Create", "Update", "Patch"}, "username")
    @classmethod
    def validate_username(cls, v):
        if len(v) < 3:
            raise ValueError
        return v


@view("Out", exclude={"password"})
class UserOut(User):
    pass


@view("Create", exclude={"id"})
class UserCreate(User):
    settings: UserSettings = Field(default_factory=UserSettings)


@view("Update", exclude={"id"})
class UserUpdate(User):
    pass


@view("Patch", exclude={"id"})
class UserPatch(User):
    username: str = None
    password: str = None
    settings: UserSettings = None


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.model_dump())
    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.model_dump())
    return db[user_id]


@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].model_dump(), **user.model_dump(exclude_unset=True)})
    return db[user_id]


def test_fastapi():
    client = TestClient(app)

    # POST
    response = client.post(
        "/users",
        json={
            "username": "admin",
            "password": "admin",
        },
    )
    assert response.status_code == 200, response.text
    assert response.json() == {
        "id": 0,
        "username": "admin",
        "settings": {"public": None},
    }

    # GET
    response = client.get("/users/0")
    assert response.status_code == 200, response.text
    assert response.json() == {
        "id": 0,
        "username": "admin",
        "settings": {"public": None},
    }

    # PUT
    response = client.put(
        "/users/0",
        json={
            "username": "superadmin",
            "password": "superadmin",
            "settings": {"public": "foo", "secret": "secret"},
        },
    )
    assert response.status_code == 200, response.text
    assert response.json() == {
        "id": 0,
        "username": "superadmin",
        "settings": {"public": "foo"},
    }

    # PATCH
    response = client.patch(
        "/users/0",
        json={
            "username": "guest",
            "settings": {"public": "bar"},
        },
    )
    assert response.status_code == 200, response.text
    assert response.json() == {
        "id": 0,
        "username": "guest",
        "settings": {"public": "bar"},
    }
```

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "pydantic-view",
    "maintainer": null,
    "docs_url": null,
    "requires_python": "<4.0,>=3.8",
    "maintainer_email": null,
    "keywords": "pydantic, model, view, utils",
    "author": "Roman Koshel",
    "author_email": "roma.koshel@gmail.com",
    "download_url": "https://files.pythonhosted.org/packages/15/ea/21092800171d05350416222981038dbfcbbb4240c01385b4e2a08b3d8975/pydantic_view-2.0.1.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 pydantic import BaseModel, Field\n   ...: from pydantic_view import view\n   ...: \n   ...: \n   ...: class User(BaseModel):\n   ...:     id: int\n   ...:     username: str\n   ...:     password: str\n   ...:     address: str\n   ...: \n   ...: @view(\"Create\", exclude={\"id\"})\n   ...: class UserCreate(User):\n   ...:     pass\n   ...:\n   ...: @view(\"Update\")\n   ...: class UserUpdate(User):\n   ...:     pass\n   ...:\n   ...: @view(\"Patch\")\n   ...: class UserPatch(User):\n   ...:     username: str = None\n   ...:     password: str = None\n   ...:     address: str = None\n   ...:\n   ...: @view(\"Out\", exclude={\"password\"})\n   ...: class UserOut(User):\n   ...:     pass\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 Optional\n\nfrom fastapi import FastAPI\nfrom fastapi.testclient import TestClient\nfrom pydantic import BaseModel, ConfigDict, Field\n\nfrom pydantic_view import view, view_field_validator\n\n\nclass UserSettings(BaseModel):\n    model_config = ConfigDict(extra=\"forbid\")\n\n    public: Optional[str] = None\n    secret: Optional[str] = None\n\n\n@view(\"Out\", exclude={\"secret\"})\nclass UserSettingsOut(UserSettings):\n    pass\n\n\n@view(\"Create\")\nclass UserSettingsCreate(UserSettings):\n    pass\n\n\n@view(\"Update\")\nclass UserSettingsUpdate(UserSettings):\n    pass\n\n\n@view(\"Patch\")\nclass UserSettingsPatch(UserSettings):\n    public: str = None\n    secret: str = None\n\n\nclass User(BaseModel):\n    model_config = ConfigDict(extra=\"forbid\")\n\n    id: int\n    username: str\n    password: str = Field(default_factory=lambda: \"password\")\n    settings: UserSettings\n\n    @view_field_validator({\"Create\", \"Update\", \"Patch\"}, \"username\")\n    @classmethod\n    def validate_username(cls, v):\n        if len(v) < 3:\n            raise ValueError\n        return v\n\n\n@view(\"Out\", exclude={\"password\"})\nclass UserOut(User):\n    pass\n\n\n@view(\"Create\", exclude={\"id\"})\nclass UserCreate(User):\n    settings: UserSettings = Field(default_factory=UserSettings)\n\n\n@view(\"Update\", exclude={\"id\"})\nclass UserUpdate(User):\n    pass\n\n\n@view(\"Patch\", exclude={\"id\"})\nclass UserPatch(User):\n    username: str = None\n    password: str = None\n    settings: UserSettings = None\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.model_dump())\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.model_dump())\n    return db[user_id]\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].model_dump(), **user.model_dump(exclude_unset=True)})\n    return db[user_id]\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, response.text\n    assert response.json() == {\n        \"id\": 0,\n        \"username\": \"admin\",\n        \"settings\": {\"public\": None},\n    }\n\n    # GET\n    response = client.get(\"/users/0\")\n    assert response.status_code == 200, response.text\n    assert response.json() == {\n        \"id\": 0,\n        \"username\": \"admin\",\n        \"settings\": {\"public\": None},\n    }\n\n    # PUT\n    response = client.put(\n        \"/users/0\",\n        json={\n            \"username\": \"superadmin\",\n            \"password\": \"superadmin\",\n            \"settings\": {\"public\": \"foo\", \"secret\": \"secret\"},\n        },\n    )\n    assert response.status_code == 200, response.text\n    assert response.json() == {\n        \"id\": 0,\n        \"username\": \"superadmin\",\n        \"settings\": {\"public\": \"foo\"},\n    }\n\n    # PATCH\n    response = client.patch(\n        \"/users/0\",\n        json={\n            \"username\": \"guest\",\n            \"settings\": {\"public\": \"bar\"},\n        },\n    )\n    assert response.status_code == 200, response.text\n    assert response.json() == {\n        \"id\": 0,\n        \"username\": \"guest\",\n        \"settings\": {\"public\": \"bar\"},\n    }\n```\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "View decorator to create the child pydantic models from the root model.",
    "version": "2.0.1",
    "project_urls": null,
    "split_keywords": [
        "pydantic",
        " model",
        " view",
        " utils"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "a5c920656e45feab1c718e6c517b0eaae16e83637669e892497855bc6d0b328e",
                "md5": "b32bdda3d6b9fe3d74759b468b5e909c",
                "sha256": "2c5d1e5caa6ff87c0d060cb370b0c23c27d43e829596ebe69245ce7189fb77dc"
            },
            "downloads": -1,
            "filename": "pydantic_view-2.0.1-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "b32bdda3d6b9fe3d74759b468b5e909c",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": "<4.0,>=3.8",
            "size": 5957,
            "upload_time": "2024-10-14T14:34:37",
            "upload_time_iso_8601": "2024-10-14T14:34:37.532240Z",
            "url": "https://files.pythonhosted.org/packages/a5/c9/20656e45feab1c718e6c517b0eaae16e83637669e892497855bc6d0b328e/pydantic_view-2.0.1-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "15ea21092800171d05350416222981038dbfcbbb4240c01385b4e2a08b3d8975",
                "md5": "d775a8e899273410d169836b8d54d180",
                "sha256": "e3b3cd3a079b5a977f2168b157aa599ba205a458dace128a15c45d02fc9778b3"
            },
            "downloads": -1,
            "filename": "pydantic_view-2.0.1.tar.gz",
            "has_sig": false,
            "md5_digest": "d775a8e899273410d169836b8d54d180",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": "<4.0,>=3.8",
            "size": 5430,
            "upload_time": "2024-10-14T14:34:39",
            "upload_time_iso_8601": "2024-10-14T14:34:39.033187Z",
            "url": "https://files.pythonhosted.org/packages/15/ea/21092800171d05350416222981038dbfcbbb4240c01385b4e2a08b3d8975/pydantic_view-2.0.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-10-14 14:34:39",
    "github": false,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "lcname": "pydantic-view"
}
        
Elapsed time: 0.34615s