Merge pull request #97 from grillazz/96-migrate-to-pydantic-2

96 migrate to pydantic 2
This commit is contained in:
Jakub Miazek 2023-07-07 20:39:32 +02:00 committed by GitHub
commit d9e88d8ffc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 352 additions and 325 deletions

1
.env
View File

@ -6,6 +6,7 @@ SQL_TEST_DB=testdb
SQL_HOST=db SQL_HOST=db
SQL_USER=user SQL_USER=user
SQL_PASS=secret SQL_PASS=secret
SQL_URL=postgresql+asyncpg://${SQL_USER}:${SQL_PASS}@${SQL_HOST}/${SQL_DB}
ALGORITHM=HS256 ALGORITHM=HS256
ACCESS_TOKEN_EXPIRE_MINUTES=30 ACCESS_TOKEN_EXPIRE_MINUTES=30

View File

@ -13,7 +13,7 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
python-version: [ "3.11" ] python-version: [ "3.11" ]
poetry-version: [ "1.4.0" ] poetry-version: [ "1.5.1" ]
env: env:
PYTHONDONTWRITEBYTECODE: 1 PYTHONDONTWRITEBYTECODE: 1
@ -23,6 +23,7 @@ jobs:
SQL_USER: app-user SQL_USER: app-user
POSTGRES_PASSWORD: secret POSTGRES_PASSWORD: secret
PGPASSWORD: secret PGPASSWORD: secret
SQL_URL: postgresql+asyncpg://app-user:secret@localhost:5432/testdb
services: services:
sqldb: sqldb:

View File

@ -7,7 +7,7 @@
![fastapi-sqlalchemy-asyncpg](/static/fsap_1.jpg) ![fastapi-sqlalchemy-asyncpg](/static/fsap_1.jpg)
Example of [FastAPI](https://fastapi.tiangolo.com/) integration supported by almighty [Pydantic](https://github.com/pydantic/pydantic) Example of [FastAPI](https://fastapi.tiangolo.com/) integration supported by almighty [Pydantic 2.0](https://github.com/pydantic/pydantic)
with [SQLAlchemy ORM](https://www.sqlalchemy.org/) and PostgreSQL with [SQLAlchemy ORM](https://www.sqlalchemy.org/) and PostgreSQL
connected via fastest Database Client Library for python/asyncio [asyncpg](https://github.com/MagicStack/asyncpg). connected via fastest Database Client Library for python/asyncio [asyncpg](https://github.com/MagicStack/asyncpg).
@ -81,6 +81,7 @@ Hope you enjoy it.
- 14 FEB 2023 bump project to Python 3.11 - 14 FEB 2023 bump project to Python 3.11
- 10 APR 2023 implement logging with rich - 10 APR 2023 implement logging with rich
- 28 APR 2023 Rainbow logs with rich :rainbow: - 28 APR 2023 Rainbow logs with rich :rainbow:
- 7 JUL 2023 migrate to pydantic 2.0
### Local development with poetry ### Local development with poetry

View File

@ -36,7 +36,7 @@ async def update_nonsense(
db_session: AsyncSession = Depends(get_db), db_session: AsyncSession = Depends(get_db),
): ):
nonsense = await Nonsense.find(db_session, name) nonsense = await Nonsense.find(db_session, name)
await nonsense.update(db_session, **payload.dict()) await nonsense.update(db_session, **payload.model_dump())
return nonsense return nonsense
@ -45,6 +45,6 @@ async def merge_nonsense(
payload: NonsenseSchema, payload: NonsenseSchema,
db_session: AsyncSession = Depends(get_db), db_session: AsyncSession = Depends(get_db),
): ):
nonsense = Nonsense(**payload.dict()) nonsense = Nonsense(**payload.model_dump())
await nonsense.save_or_update(db_session) await nonsense.save_or_update(db_session)
return nonsense return nonsense

View File

@ -1,7 +1,6 @@
from typing import Annotated from typing import Annotated
from fastapi import APIRouter, Depends, Query from fastapi import APIRouter, Depends, Query
from pydantic import Required
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from app.database import get_db from app.database import get_db
@ -14,7 +13,7 @@ router = APIRouter(prefix="/v1/shakespeare")
"/", "/",
) )
async def find_paragraph( async def find_paragraph(
character: Annotated[str, Query(description="Character name")] = Required, character: Annotated[str, Query(description="Character name")],
db_session: AsyncSession = Depends(get_db), db_session: AsyncSession = Depends(get_db),
): ):
return await Paragraph.find(db_session=db_session, character=character) return await Paragraph.find(db_session=db_session, character=character)

View File

@ -54,5 +54,5 @@ async def update_stuff(
db_session: AsyncSession = Depends(get_db), db_session: AsyncSession = Depends(get_db),
): ):
stuff = await Stuff.find(db_session, name) stuff = await Stuff.find(db_session, name)
await stuff.update(db_session, **payload.dict()) await stuff.update(db_session, **payload.model_dump())
return stuff return stuff

View File

@ -1,18 +1,12 @@
import os import os
from functools import lru_cache from functools import lru_cache
from pydantic import BaseSettings, PostgresDsn from pydantic import PostgresDsn
from pydantic_settings import BaseSettings
class Settings(BaseSettings): class Settings(BaseSettings):
asyncpg_url: PostgresDsn = PostgresDsn.build( asyncpg_url: PostgresDsn = os.getenv("SQL_URL")
scheme="postgresql+asyncpg",
user=os.getenv("SQL_USER"),
password=os.getenv("POSTGRES_PASSWORD"),
host=os.getenv("SQL_HOST"),
port="5432",
path=f"/{os.getenv('SQL_DB') or ''}",
)
@lru_cache @lru_cache

View File

@ -10,7 +10,7 @@ global_settings = config.get_settings()
logger = AppLogger.__call__().get_logger() logger = AppLogger.__call__().get_logger()
engine = create_async_engine( engine = create_async_engine(
global_settings.asyncpg_url, global_settings.asyncpg_url.unicode_string(),
future=True, future=True,
echo=True, echo=True,
) )

View File

@ -14,8 +14,8 @@ class NonsenseSchema(BaseModel):
) )
class Config: class Config:
orm_mode = True from_attributes = True
schema_extra = { json_schema_extra = {
"example": { "example": {
"name": "Name for Some Nonsense", "name": "Name for Some Nonsense",
"description": "Some Nonsense Description", "description": "Some Nonsense Description",
@ -38,8 +38,8 @@ class NonsenseResponse(BaseModel):
) )
class Config: class Config:
orm_mode = True from_attributes = True
schema_extra = { json_schema_extra = {
"example": { "example": {
"config_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "config_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"name": "Name for Some Nonsense", "name": "Name for Some Nonsense",

View File

@ -14,8 +14,8 @@ class StuffSchema(BaseModel):
) )
class Config: class Config:
orm_mode = True from_attributes = True
schema_extra = { json_schema_extra = {
"example": { "example": {
"name": "Name for Some Stuff", "name": "Name for Some Stuff",
"description": "Some Stuff Description", "description": "Some Stuff Description",
@ -38,8 +38,8 @@ class StuffResponse(BaseModel):
) )
class Config: class Config:
orm_mode = True from_attributes = True
schema_extra = { json_schema_extra = {
"example": { "example": {
"config_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "config_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"name": "Name for Some Stuff", "name": "Name for Some Stuff",

628
poetry.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "fastapi-sqlalchemy-asyncpg" name = "fastapi-sqlalchemy-asyncpg"
version = "0.0.4" version = "0.0.5"
description = "" description = ""
authors = ["Jakub Miazek <the@grillazz.com>"] authors = ["Jakub Miazek <the@grillazz.com>"]
packages = [] packages = []
@ -26,6 +26,7 @@ ipython = "*"
pytest-cov = "*" pytest-cov = "*"
pytest-asyncio = "*" pytest-asyncio = "*"
ruff = "*" ruff = "*"
pydantic-settings = "^2.0.1"
[tool.poetry.group.dev.dependencies] [tool.poetry.group.dev.dependencies]
tryceratops = "^1.1.0" tryceratops = "^1.1.0"