Skip to content

Commit b3c664e

Browse files
authored
Merge pull request #213 from grillazz/12-add-json-field-example
wip: crud refactor
2 parents 5630196 + 58553c6 commit b3c664e

File tree

8 files changed

+64
-99
lines changed

8 files changed

+64
-99
lines changed

app/api/stuff.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,5 +116,5 @@ async def update_stuff(
116116
db_session: AsyncSession = Depends(get_db),
117117
):
118118
stuff = await Stuff.find(db_session, name)
119-
await stuff.update(db_session, **payload.model_dump())
119+
await stuff.update(**payload.model_dump())
120120
return stuff

app/database.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from collections.abc import AsyncGenerator
22

33
from rotoger import AppStructLogger
4+
from sqlalchemy.exc import SQLAlchemyError
45
from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine
56

67
from app.config import settings as global_settings
@@ -28,6 +29,12 @@ async def get_db() -> AsyncGenerator:
2829
# logger.debug(f"ASYNC Pool: {engine.pool.status()}")
2930
try:
3031
yield session
31-
except Exception as e:
32-
await logger.aerror(f"Error getting database session: {e}")
33-
raise
32+
await session.commit()
33+
except Exception as ex:
34+
if isinstance(ex, SQLAlchemyError):
35+
# Re-raise SQLAlchemyError directly without handling
36+
raise
37+
else:
38+
# Handle other exceptions
39+
await logger.aerror(f"NonSQLAlchemyError: {repr(ex)}")
40+
raise # Re-raise after logging

app/exception_handlers.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import orjson
2+
from fastapi import FastAPI, Request
3+
from fastapi.responses import JSONResponse
4+
from rotoger import AppStructLogger
5+
from sqlalchemy.exc import SQLAlchemyError
6+
7+
logger = AppStructLogger().get_logger()
8+
9+
10+
async def sqlalchemy_exception_handler(
11+
request: Request, exc: SQLAlchemyError
12+
) -> JSONResponse:
13+
request_path = request.url.path
14+
try:
15+
raw_body = await request.body()
16+
request_body = orjson.loads(raw_body) if raw_body else None
17+
except orjson.JSONDecodeError:
18+
request_body = None
19+
20+
await logger.aerror(
21+
"Database error occurred",
22+
sql_error=repr(exc),
23+
request_url=request_path,
24+
request_body=request_body,
25+
)
26+
27+
return JSONResponse(
28+
status_code=500,
29+
content={"message": "A database error occurred. Please try again later."},
30+
)
31+
32+
33+
def register_exception_handlers(app: FastAPI) -> None:
34+
"""Register all exception handlers with the FastAPI app."""
35+
app.add_exception_handler(SQLAlchemyError, sqlalchemy_exception_handler)

app/exceptions.py

Lines changed: 0 additions & 59 deletions
This file was deleted.

app/main.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from app.api.stuff import router as stuff_router
1515
from app.api.user import router as user_router
1616
from app.config import settings as global_settings
17+
from app.exception_handlers import register_exception_handlers
1718
from app.redis import get_redis
1819
from app.services.auth import AuthBearer
1920

@@ -61,6 +62,9 @@ def create_app() -> FastAPI:
6162
dependencies=[Depends(AuthBearer())],
6263
)
6364

65+
# Register exception handlers
66+
register_exception_handlers(app)
67+
6468
@app.get("/index", response_class=HTMLResponse)
6569
def get_index(request: Request):
6670
return templates.TemplateResponse("index.html", {"request": request})

app/models/base.py

Lines changed: 11 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -20,64 +20,40 @@ def __tablename__(self) -> str:
2020
return self.__name__.lower()
2121

2222
async def save(self, db_session: AsyncSession):
23-
"""
24-
25-
:param db_session:
26-
:return:
27-
"""
28-
try:
29-
db_session.add(self)
30-
await db_session.commit()
31-
await db_session.refresh(self)
32-
return self
33-
except SQLAlchemyError as ex:
34-
await logger.aerror(f"Error inserting instance of {self}: {repr(ex)}")
35-
raise HTTPException(
36-
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail=repr(ex)
37-
) from ex
23+
db_session.add(self)
24+
await db_session.flush()
25+
await db_session.refresh(self)
26+
return self
3827

3928
async def delete(self, db_session: AsyncSession):
40-
"""
41-
42-
:param db_session:
43-
:return:
44-
"""
4529
try:
4630
await db_session.delete(self)
47-
await db_session.commit()
4831
return True
4932
except SQLAlchemyError as ex:
5033
raise HTTPException(
5134
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail=repr(ex)
5235
) from ex
5336

54-
async def update(self, db: AsyncSession, **kwargs):
55-
"""
56-
57-
:param db:
58-
:param kwargs
59-
:return:
60-
"""
37+
async def update(self, **kwargs):
6138
try:
6239
for k, v in kwargs.items():
6340
setattr(self, k, v)
64-
return await db.commit()
41+
return True
6542
except SQLAlchemyError as ex:
6643
raise HTTPException(
6744
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail=repr(ex)
6845
) from ex
6946

70-
async def save_or_update(self, db: AsyncSession):
47+
async def save_or_update(self, db_session: AsyncSession):
7148
try:
72-
db.add(self)
73-
return await db.commit()
49+
db_session.add(self)
50+
await db_session.flush()
51+
return True
7452
except IntegrityError as exception:
7553
if isinstance(exception.orig, UniqueViolationError):
76-
return await db.merge(self)
54+
return await db_session.merge(self)
7755
else:
7856
raise HTTPException(
7957
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
8058
detail=repr(exception),
8159
) from exception
82-
finally:
83-
await db.close()

app/schemas/stuff.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77

88

99
class RandomStuff(BaseModel):
10-
chaos: dict[str, Any] = Field(..., description="JSON data for chaos field")
10+
chaos: dict[str, Any] = Field(
11+
..., description="Pretty chaotic JSON data can be added here..."
12+
)
1113

1214

1315
class StuffSchema(BaseModel):

tests/api/test_chaotic_stuff.py

Whitespace-only changes.

0 commit comments

Comments
 (0)