Name | pydantic-view JSON |
Version |
0.3.0
JSON |
| download |
home_page | |
Summary | View decorator to create the child pydantic models from the root model. |
upload_time | 2023-08-18 17:40:56 |
maintainer | |
docs_url | None |
author | Roman Koshel |
requires_python | >=3.8,<4.0 |
license | MIT |
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"
}