mirror of
https://github.com/fastapi-practices/fastapi_best_architecture.git
synced 2025-08-14 19:04:00 +08:00
73 lines
2.0 KiB
Python
73 lines
2.0 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
from decimal import Decimal
|
|
from typing import Any, Sequence, TypeVar
|
|
|
|
from fastapi.encoders import decimal_encoder
|
|
from msgspec import json
|
|
from sqlalchemy import Row, RowMapping
|
|
from sqlalchemy.orm import ColumnProperty, SynonymProperty, class_mapper
|
|
from starlette.responses import JSONResponse
|
|
|
|
RowData = Row | RowMapping | Any
|
|
|
|
R = TypeVar('R', bound=RowData)
|
|
|
|
|
|
def select_columns_serialize(row: R) -> dict[str, Any]:
|
|
"""
|
|
序列化 SQLAlchemy 查询表的列,不包含关联列
|
|
|
|
:param row: SQLAlchemy 查询结果行
|
|
:return:
|
|
"""
|
|
result = {}
|
|
for column in row.__table__.columns.keys():
|
|
value = getattr(row, column)
|
|
if isinstance(value, Decimal):
|
|
value = decimal_encoder(value)
|
|
result[column] = value
|
|
return result
|
|
|
|
|
|
def select_list_serialize(row: Sequence[R]) -> list[dict[str, Any]]:
|
|
"""
|
|
序列化 SQLAlchemy 查询列表
|
|
|
|
:param row: SQLAlchemy 查询结果列表
|
|
:return:
|
|
"""
|
|
return [select_columns_serialize(item) for item in row]
|
|
|
|
|
|
def select_as_dict(row: R, use_alias: bool = False) -> dict[str, Any]:
|
|
"""
|
|
将 SQLAlchemy 查询结果转换为字典,可以包含关联数据
|
|
|
|
:param row: SQLAlchemy 查询结果行
|
|
:param use_alias: 是否使用别名作为列名
|
|
:return:
|
|
"""
|
|
if not use_alias:
|
|
result = row.__dict__
|
|
if '_sa_instance_state' in result:
|
|
del result['_sa_instance_state']
|
|
else:
|
|
result = {}
|
|
mapper = class_mapper(row.__class__) # type: ignore
|
|
for prop in mapper.iterate_properties:
|
|
if isinstance(prop, (ColumnProperty, SynonymProperty)):
|
|
key = prop.key
|
|
result[key] = getattr(row, key)
|
|
|
|
return result
|
|
|
|
|
|
class MsgSpecJSONResponse(JSONResponse):
|
|
"""
|
|
使用高性能的 msgspec 库将数据序列化为 JSON 的响应类
|
|
"""
|
|
|
|
def render(self, content: Any) -> bytes:
|
|
return json.encode(content)
|