Skip to content

Commit

Permalink
V0.2.9 (#26)
Browse files Browse the repository at this point in the history
* add ExceptionHandler

* update exception

* fix ExceptionHandler

* model create support nested

* update exception code

* update version
  • Loading branch information
taogeYT authored Dec 19, 2024
1 parent efa2158 commit 8c38495
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 20 deletions.
29 changes: 21 additions & 8 deletions appboot/asgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,24 @@
from appboot.exceptions import Error


class ExceptionHandler:
exceptions = [Error, Exception]

def __init__(self, request: Request, exc: Exception):
self.request = request
self.exc = exc

async def make_response(self):
status_code = 500
if isinstance(self.exc, Error):
status_code = self.exc.code
return JSONResponse({'detail': str(self.exc)}, status_code=status_code)

@classmethod
async def handle_exception(cls, request: Request, exc: Exception):
return await cls(request, exc).make_response()


def get_asgi_application():
return get_fastapi_application()

Expand All @@ -33,14 +51,9 @@ def get_fastapi_application():
return app


def fastapi_register_exception(app: FastAPI):
@app.exception_handler(Error)
async def app_error_handler(request: Request, exc: Error):
return JSONResponse({'detail': str(exc)}, status_code=400)

@app.exception_handler(Exception)
async def app_exception_handler(request: Request, exc: Exception):
return JSONResponse({'detail': str(exc)}, status_code=500)
def fastapi_register_exception(app: FastAPI, handler=ExceptionHandler):
for exc_cls in handler.exceptions:
app.exception_handler(exc_cls)(handler.handle_exception)


def fastapi_register_routers(app: FastAPI):
Expand Down
32 changes: 28 additions & 4 deletions appboot/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,41 @@
class Error(Exception):
pass
code = 500


class NotFound(Error):
code = 404


class BadRequest(Error):
code = 400


class Unauthorized(Error):
code = 401


class Forbidden(Error):
code = 403


class Conflict(Error):
code = 409


class Unavailable(Error):
code = 422


class FilterError(Error):
pass
code = 500


class InterfaceError(Error):
pass
code = 500


class DatabaseError(Error):
pass
code = 400


class DoesNotExist(DatabaseError):
Expand Down
24 changes: 24 additions & 0 deletions appboot/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ class Model(Base):
id: Mapped[int] = mapped_column(primary_key=True)
objects: typing.ClassVar[QuerySet[Self]] = QuerySetProperty(ScopedSession) # type: ignore

@classmethod
def construct(cls, **kwargs: dict[str, typing.Any]) -> Self:
return _parse_data_to_model(cls, kwargs)

async def update(self, **values: dict[str, typing.Any]):
for name, value in values.items():
if hasattr(self, name) and getattr(self, name) != value:
Expand All @@ -72,3 +76,23 @@ async def save(self):
session = ScopedSession()
session.add(self)
await session.flush()


def _parse_data_to_model(model: type[Base], data: dict[str, typing.Any]):
_data = {}
for key, column in model.__mapper__.columns.items():
if key not in data or column.primary_key:
continue
_data[key] = data[key]
for key, rel in model.__mapper__.relationships.items():
if key not in data:
continue
sub_model = rel.mapper.class_
if rel.uselist:
rel_result = [
_parse_data_to_model(sub_model, sub_data) for sub_data in data[key]
]
else:
rel_result = _parse_data_to_model(sub_model, data[key])
_data[key] = rel_result
return model(**_data)
9 changes: 4 additions & 5 deletions appboot/repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,9 @@ async def _paginate(self, query: PaginationQuerySchema, must_count: bool = True)
count=count, results=results, page=query.page, page_size=query.page_size
)

async def paginate(self, query: PaginationQuerySchema, must_count: bool = True) -> PaginationResult[ModelT]:
async def paginate(
self, query: PaginationQuerySchema, must_count: bool = True
) -> PaginationResult[ModelT]:
return await self.filter_query(query)._paginate(query, must_count)

async def all(self) -> list[ModelT]:
Expand All @@ -147,10 +149,7 @@ async def get(self, **kwargs) -> ModelT:
return result

async def create(self, **kwargs) -> ModelT:
# todo 支持关联关系一同创建
instance = self._model(
**{k: v for k, v in kwargs.items() if k in self.model.__mapper__.columns}
)
instance = self._model.construct(**kwargs)
self._query.session.add(instance)
await self._session.flush([instance])
await self._session.refresh(instance)
Expand Down
4 changes: 1 addition & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "appboot"
version = "0.2.8"
version = "0.2.9"
description = "Use FastAPI like Django"
authors = ["liyatao <liyatao001@gmail.com>"]
readme = "README.md"
Expand Down Expand Up @@ -39,7 +39,6 @@ build-backend = "poetry.core.masonry.api"
[tool.poetry.scripts]
appboot = 'appboot.commands:app'


[tool.ruff]
line-length = 88
target-version = 'py39'
Expand All @@ -55,7 +54,6 @@ known-third-party = ["fastapi", "pydantic", "sqlalchemy"]
[tool.ruff.format]
quote-style = "single"


[tool.mypy]
python_version = "3.9"
plugins = ["pydantic.mypy"]
Expand Down

0 comments on commit 8c38495

Please sign in to comment.