fastapi-augment 0.1.0__tar.gz → 0.1.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/PKG-INFO +103 -73
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/README.md +99 -72
- fastapi_augment-0.1.2/VERSION +1 -0
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/pyproject.toml +8 -3
- fastapi_augment-0.1.2/src/fastapi_augment/common/utils/__init__.py +32 -0
- fastapi_augment-0.1.2/src/fastapi_augment/common/utils/paths.py +36 -0
- fastapi_augment-0.1.2/src/fastapi_augment/db/__init__.py +6 -0
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/src/fastapi_augment/db/sqlalchemy/__init__.py +5 -5
- fastapi_augment-0.1.2/src/fastapi_augment/db/sqlalchemy/alembic/README +1 -0
- fastapi_augment-0.1.2/src/fastapi_augment/db/sqlalchemy/alembic/script.py.mako +28 -0
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/src/fastapi_augment/db/sqlalchemy/engine.py +13 -6
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/src/fastapi_augment/db/sqlalchemy/mixins/audit.py +2 -2
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/src/fastapi_augment/db/sqlalchemy/mixins/soft_delete.py +4 -4
- fastapi_augment-0.1.0/src/fastapi_augment/db/sqlalchemy/crud_base.py → fastapi_augment-0.1.2/src/fastapi_augment/db/sqlalchemy/repository_base.py +49 -16
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/src/fastapi_augment/db/sqlalchemy/session.py +34 -21
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/src/fastapi_augment/factory.py +12 -5
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/src/fastapi_augment/log/config.py +9 -1
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/src/fastapi_augment/log/factory.py +8 -1
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/src/fastapi_augment/log/handlers.py +17 -5
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/src/fastapi_augment.egg-info/PKG-INFO +103 -73
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/src/fastapi_augment.egg-info/SOURCES.txt +9 -3
- fastapi_augment-0.1.2/tests/test_db_repository.py +412 -0
- fastapi_augment-0.1.2/tests/test_migrate.py +80 -0
- fastapi_augment-0.1.2/tests/test_model_base.py +89 -0
- fastapi_augment-0.1.2/tests/test_settings.py +78 -0
- fastapi_augment-0.1.0/VERSION +0 -1
- fastapi_augment-0.1.0/src/fastapi_augment/common/utils/__init__.py +0 -5
- fastapi_augment-0.1.0/src/fastapi_augment/db/__init__.py +0 -5
- fastapi_augment-0.1.0/tests/test_db_crud.py +0 -348
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/setup.cfg +0 -0
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/src/fastapi_augment/__init__.py +0 -0
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/src/fastapi_augment/common/__init__.py +0 -0
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/src/fastapi_augment/common/constants.py +0 -0
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/src/fastapi_augment/common/exception_handlers.py +0 -0
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/src/fastapi_augment/common/exceptions.py +0 -0
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/src/fastapi_augment/common/utils/strings.py +0 -0
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/src/fastapi_augment/config/__init__.py +0 -0
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/src/fastapi_augment/config/settings.py +0 -0
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/src/fastapi_augment/db/sqlalchemy/alembic/__init__.py +0 -0
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/src/fastapi_augment/db/sqlalchemy/alembic/env.py +0 -0
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/src/fastapi_augment/db/sqlalchemy/base.py +0 -0
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/src/fastapi_augment/db/sqlalchemy/migrate.py +0 -0
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/src/fastapi_augment/db/sqlalchemy/mixins/__init__.py +0 -0
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/src/fastapi_augment/db/sqlalchemy/mixins/timestamp.py +0 -0
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/src/fastapi_augment/db/sqlalchemy/model_base.py +0 -0
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/src/fastapi_augment/health/__init__.py +0 -0
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/src/fastapi_augment/health/checker.py +0 -0
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/src/fastapi_augment/health/checkers.py +0 -0
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/src/fastapi_augment/health/router.py +0 -0
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/src/fastapi_augment/lifespan.py +0 -0
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/src/fastapi_augment/log/__init__.py +0 -0
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/src/fastapi_augment/log/filters.py +0 -0
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/src/fastapi_augment/middlewares/__init__.py +0 -0
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/src/fastapi_augment/middlewares/base.py +0 -0
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/src/fastapi_augment/middlewares/request_id.py +0 -0
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/src/fastapi_augment/openapi.py +0 -0
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/src/fastapi_augment/py.typed +0 -0
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/src/fastapi_augment/schemas/__init__.py +0 -0
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/src/fastapi_augment/schemas/base.py +0 -0
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/src/fastapi_augment/schemas/pagination.py +0 -0
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/src/fastapi_augment/schemas/request.py +0 -0
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/src/fastapi_augment/schemas/response.py +0 -0
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/src/fastapi_augment/schemas/types.py +0 -0
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/src/fastapi_augment.egg-info/dependency_links.txt +0 -0
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/src/fastapi_augment.egg-info/entry_points.txt +0 -0
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/src/fastapi_augment.egg-info/requires.txt +0 -0
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/src/fastapi_augment.egg-info/top_level.txt +0 -0
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/tests/test_config.py +0 -0
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/tests/test_constants.py +0 -0
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/tests/test_db_engine.py +0 -0
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/tests/test_db_session_models.py +0 -0
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/tests/test_exception_handlers.py +0 -0
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/tests/test_exceptions.py +0 -0
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/tests/test_factory.py +0 -0
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/tests/test_health.py +0 -0
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/tests/test_lifespan.py +0 -0
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/tests/test_log.py +0 -0
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/tests/test_middlewares.py +0 -0
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/tests/test_openapi.py +0 -0
- {fastapi_augment-0.1.0 → fastapi_augment-0.1.2}/tests/test_schemas.py +0 -0
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fastapi-augment
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: FastAPI 通用代码工具包,跨项目复用
|
|
5
5
|
Author-email: zarkhan <hanguangzheng@qq.com>
|
|
6
6
|
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/hgz1989/fastapi_augment
|
|
8
|
+
Project-URL: Repository, https://github.com/hgz1989/fastapi_augment
|
|
9
|
+
Project-URL: Documentation, https://github.com/hgz1989/fastapi_augment#readme
|
|
7
10
|
Keywords: fastapi,fastapi-augment,augment,web,framework,async
|
|
8
11
|
Classifier: Development Status :: 3 - Alpha
|
|
9
12
|
Classifier: Framework :: FastAPI
|
|
@@ -40,13 +43,13 @@ Requires-Dist: fastapi-augment[config,orjson,sqlalchemy,uvicorn]; extra == "stan
|
|
|
40
43
|
|
|
41
44
|
- **应用工厂** — 一行代码创建 FastAPI 实例,自动装配中间件、路由、生命周期与数据库
|
|
42
45
|
- **生命周期管理** — 多注册表、优先级、超时控制、异常策略的启动/关闭钩子
|
|
43
|
-
- **读写分离** — 单库 / 主从 /
|
|
44
|
-
-
|
|
46
|
+
- **读写分离** — 单库 / 主从 / 集群拓扑的异步引擎管理,线程安全的 Session 自动路由
|
|
47
|
+
- **泛型仓储** — 类型安全的异步 Repository,支持直接实例化与子类继承两种方式
|
|
45
48
|
- **可组合 Mixin** — 时间戳、审计、软删除等列混入,自由组合
|
|
46
49
|
- **统一响应** — 全局 `APIResponse` 格式,自动追踪 `request_id`
|
|
47
50
|
- **HTTP 异常** — 完整的 4xx 异常子类,内置默认文案
|
|
48
51
|
- **OpenAPI 优化** — 自动清理 422 响应、可选 Bearer 认证
|
|
49
|
-
- **日志管理** — request_id 自动注入、uvicorn
|
|
52
|
+
- **日志管理** — request_id 自动注入、uvicorn 接管、多进程安全轮转、幂等初始化、一键配置
|
|
50
53
|
- **健康检查** — 可扩展的检查器模式,内置应用状态与数据库连通性检查,一行开关
|
|
51
54
|
- **配置管理** — 基于 pydantic-settings,支持 `.env` 文件、环境变量前缀、嵌套配置
|
|
52
55
|
- **数据库迁移 CLI** — 一行命令生成/执行迁移,自动发现用户模型
|
|
@@ -75,8 +78,9 @@ from fastapi import APIRouter
|
|
|
75
78
|
from fastapi_augment import create_app
|
|
76
79
|
from fastapi_augment.db.sqlalchemy import (
|
|
77
80
|
ClusterTopology, NodeConfig, EngineManager, SessionFactory,
|
|
78
|
-
ModelBase,
|
|
81
|
+
ModelBase, RepositoryBase,
|
|
79
82
|
)
|
|
83
|
+
from fastapi_augment.db.sqlalchemy.mixins import TimestampMixin
|
|
80
84
|
from fastapi_augment.schemas import response_success
|
|
81
85
|
|
|
82
86
|
# ── 1. 数据库拓扑 ──────────────────────────────────────
|
|
@@ -95,13 +99,13 @@ class User(TimestampMixin, ModelBase):
|
|
|
95
99
|
|
|
96
100
|
# ── 3. 路由 ────────────────────────────────────────────
|
|
97
101
|
router = APIRouter()
|
|
98
|
-
|
|
102
|
+
user_repo = RepositoryBase(User)
|
|
99
103
|
|
|
100
104
|
|
|
101
105
|
@router.get('/users')
|
|
102
106
|
async def list_users():
|
|
103
107
|
async with sessions.read_session() as session:
|
|
104
|
-
users = await
|
|
108
|
+
users = await user_repo.list(session, is_active=True, limit=10)
|
|
105
109
|
return response_success(data=users)
|
|
106
110
|
|
|
107
111
|
|
|
@@ -121,14 +125,14 @@ app = create_app(
|
|
|
121
125
|
|
|
122
126
|
统一创建 FastAPI 实例,自动装配以下组件:
|
|
123
127
|
|
|
124
|
-
| 组件
|
|
125
|
-
|
|
126
|
-
| 生命周期 | 接入 `fastapi_lifespan`,合并用户注册表与 `core_registry`
|
|
127
|
-
| 中间件
|
|
128
|
-
| 路由
|
|
129
|
-
| OpenAPI
|
|
130
|
-
| 数据库
|
|
131
|
-
| 健康检查 | `health_check=True` 一键启用 `/health` 端点
|
|
128
|
+
| 组件 | 说明 |
|
|
129
|
+
| -------- | ---------------------------------------------------------- |
|
|
130
|
+
| 生命周期 | 接入 `fastapi_lifespan`,合并用户注册表与 `core_registry` |
|
|
131
|
+
| 中间件 | 自动添加 `RequestIdMiddleware`,可选 CORS |
|
|
132
|
+
| 路由 | 支持 `APIRouter` 列表或 `(router, kwargs)` 元组 |
|
|
133
|
+
| OpenAPI | 自动清理 422 响应、可选 Bearer 认证 |
|
|
134
|
+
| 数据库 | 可选挂载 `EngineManager` / `SessionFactory` 到 `app.state` |
|
|
135
|
+
| 健康检查 | `health_check=True` 一键启用 `/health` 端点 |
|
|
132
136
|
|
|
133
137
|
```python
|
|
134
138
|
from fastapi_augment import create_app, HookRegistry
|
|
@@ -141,7 +145,7 @@ async def init_cache() -> None:
|
|
|
141
145
|
|
|
142
146
|
app = create_app(
|
|
143
147
|
title='My Service',
|
|
144
|
-
registries=
|
|
148
|
+
registries=registry, # 单个或列表均可
|
|
145
149
|
cors_allow_origins=['*'],
|
|
146
150
|
openapi_enable_bearer_auth=True,
|
|
147
151
|
health_check=True, # 启用健康检查
|
|
@@ -200,7 +204,7 @@ topology = ClusterTopology(
|
|
|
200
204
|
)
|
|
201
205
|
|
|
202
206
|
manager = EngineManager(topology).start()
|
|
203
|
-
# 读引擎轮询(round-robin
|
|
207
|
+
# 读引擎轮询(round-robin),线程安全
|
|
204
208
|
read_engine = manager.next_read_engine()
|
|
205
209
|
```
|
|
206
210
|
|
|
@@ -231,6 +235,8 @@ async def list_users(session: AsyncSession = Depends(sessions.depends_read)):
|
|
|
231
235
|
...
|
|
232
236
|
```
|
|
233
237
|
|
|
238
|
+
> **缓存说明:** `read_session()` 按 `AsyncEngine` 缓存 `async_sessionmaker`,线程安全,避免重复创建。打印 `SessionFactory` 实例可查看已缓存的引擎信息。
|
|
239
|
+
|
|
234
240
|
#### 模型基类 — `ModelBase`
|
|
235
241
|
|
|
236
242
|
基于 ULID 主键的声明式模型基类:
|
|
@@ -243,61 +249,74 @@ class User(TimestampMixin, ModelBase):
|
|
|
243
249
|
name: str
|
|
244
250
|
```
|
|
245
251
|
|
|
246
|
-
####
|
|
252
|
+
#### 泛型仓储 — `RepositoryBase`
|
|
247
253
|
|
|
248
|
-
|
|
254
|
+
类型安全的异步仓储基类,Repository 方法只 **flush**,不 commit,事务边界由调用方控制。
|
|
255
|
+
支持两种使用方式:
|
|
249
256
|
|
|
250
257
|
```python
|
|
251
|
-
from fastapi_augment.db.sqlalchemy import
|
|
258
|
+
from fastapi_augment.db.sqlalchemy import RepositoryBase
|
|
259
|
+
|
|
260
|
+
# 方式 1:直接实例化 — 显式传入模型类
|
|
261
|
+
user_repo = RepositoryBase(User)
|
|
262
|
+
|
|
263
|
+
# 方式 2:子类继承 — 通过泛型参数绑定模型,可扩展自定义方法
|
|
264
|
+
class UserRepo(RepositoryBase[User]):
|
|
265
|
+
async def find_by_email(self, session, email: str) -> User | None:
|
|
266
|
+
return await self.get_one(session, email=email)
|
|
252
267
|
|
|
253
|
-
|
|
268
|
+
user_repo = UserRepo() # 无需再传 User
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
**CRUD 操作:**
|
|
254
272
|
|
|
273
|
+
```python
|
|
255
274
|
# Create(静态方法)
|
|
256
275
|
async with sessions.transaction() as session:
|
|
257
|
-
await
|
|
258
|
-
await
|
|
276
|
+
await user_repo.create(session, User(name='alice'))
|
|
277
|
+
await user_repo.create_many(session, [User(name='bob'), User(name='carol')])
|
|
259
278
|
|
|
260
279
|
# Read(实例方法)
|
|
261
280
|
async with sessions.read_session() as session:
|
|
262
|
-
user = await
|
|
263
|
-
user = await
|
|
264
|
-
users = await
|
|
265
|
-
total = await
|
|
266
|
-
has_admin = await
|
|
281
|
+
user = await user_repo.get(session, id_='01HXK...')
|
|
282
|
+
user = await user_repo.get_one(session, name='alice')
|
|
283
|
+
users = await user_repo.list(session, role='admin', order_by=['-created_at'], limit=10)
|
|
284
|
+
total = await user_repo.count(session, is_active=True)
|
|
285
|
+
has_admin = await user_repo.exists(session, role='admin')
|
|
267
286
|
|
|
268
287
|
# 分页查询(返回 dict:items / page / size / total / pages)
|
|
269
|
-
result = await
|
|
288
|
+
result = await user_repo.paginate(session, page=1, size=10, role='admin', order_by=['-created_at'])
|
|
270
289
|
# result = {'items': [...], 'page': 1, 'size': 10, 'total': 100, 'pages': 10}
|
|
271
290
|
|
|
272
291
|
# Update
|
|
273
292
|
async with sessions.transaction() as session:
|
|
274
|
-
await
|
|
275
|
-
affected = await
|
|
293
|
+
await user_repo.update(session, user, name='new_name')
|
|
294
|
+
affected = await user_repo.update_by_id(session, id_='01HXK...', name='new_name')
|
|
276
295
|
|
|
277
296
|
# Delete
|
|
278
297
|
async with sessions.transaction() as session:
|
|
279
|
-
await
|
|
280
|
-
deleted = await
|
|
281
|
-
count = await
|
|
298
|
+
await user_repo.delete(session, user)
|
|
299
|
+
deleted = await user_repo.delete_by_id(session, id_='01HXK...')
|
|
300
|
+
count = await user_repo.delete_where(session, is_active=False)
|
|
282
301
|
```
|
|
283
302
|
|
|
284
303
|
**过滤语法:**
|
|
285
304
|
|
|
286
305
|
```python
|
|
287
306
|
# 关键字过滤 — 等值匹配
|
|
288
|
-
await
|
|
307
|
+
await user_repo.list(session, name='alice')
|
|
289
308
|
|
|
290
309
|
# 序列 — 自动转为 IN 查询
|
|
291
|
-
await
|
|
310
|
+
await user_repo.list(session, id_=['01HXK...', '01HXL...'])
|
|
292
311
|
|
|
293
312
|
# None — 自动转为 IS NULL
|
|
294
|
-
await
|
|
313
|
+
await user_repo.list(session, deleted_at=None)
|
|
295
314
|
|
|
296
315
|
# 原生 SQLAlchemy 表达式
|
|
297
|
-
await
|
|
316
|
+
await user_repo.list(session, expressions=(User.age > 18,))
|
|
298
317
|
|
|
299
318
|
# 排序:字段名前缀 - 表示降序
|
|
300
|
-
await
|
|
319
|
+
await user_repo.list(session, order_by=['-created_at', 'name'])
|
|
301
320
|
```
|
|
302
321
|
|
|
303
322
|
### 数据库迁移 CLI — `fastapi-augment-migrate`
|
|
@@ -381,34 +400,35 @@ fastapi-augment-migrate upgrade --project-dir /path/to/project
|
|
|
381
400
|
|
|
382
401
|
#### CLI 参数一览
|
|
383
402
|
|
|
384
|
-
| 子命令
|
|
385
|
-
|
|
386
|
-
| `init`
|
|
387
|
-
|
|
|
388
|
-
| `generate` | `--message`
|
|
389
|
-
|
|
|
390
|
-
|
|
|
391
|
-
| `upgrade`
|
|
392
|
-
|
|
|
393
|
-
|
|
|
394
|
-
|
|
|
403
|
+
| 子命令 | 参数 | 说明 |
|
|
404
|
+
| ---------- | --------------- | --------------------------------------- |
|
|
405
|
+
| `init` | `--db-url` | 数据库 URL(默认 `sqlite:///app.db`) |
|
|
406
|
+
| | `--project-dir` | 项目根目录(默认当前目录) |
|
|
407
|
+
| `generate` | `--message` | **必填**,迁移描述 |
|
|
408
|
+
| | `--models` | **必填**,模型模块路径,逗号分隔 |
|
|
409
|
+
| | `--project-dir` | 项目根目录(默认当前目录) |
|
|
410
|
+
| `upgrade` | `--db-url` | 数据库 URL(不传则从 alembic.ini 读取) |
|
|
411
|
+
| | `--revision` | 目标版本(默认 `head`) |
|
|
412
|
+
| | `--downgrade` | 降级模式 |
|
|
413
|
+
| | `--project-dir` | 项目根目录(默认当前目录) |
|
|
395
414
|
|
|
396
415
|
### 模型 Mixin — `db.sqlalchemy.mixins`
|
|
397
416
|
|
|
398
417
|
可组合的列混入,按需叠加:
|
|
399
418
|
|
|
400
|
-
| Mixin
|
|
401
|
-
|
|
402
|
-
| `CreatedAtMixin`
|
|
403
|
-
| `TimestampMixin`
|
|
404
|
-
| `CreatedByMixin`
|
|
405
|
-
| `UpdatedByMixin`
|
|
406
|
-
| `AuditMixin`
|
|
407
|
-
| `SoftDeleteMixin`
|
|
419
|
+
| Mixin | 提供的列 |
|
|
420
|
+
| ---------------------- | ------------------------------------------ |
|
|
421
|
+
| `CreatedAtMixin` | `created_at` |
|
|
422
|
+
| `TimestampMixin` | `created_at` + `updated_at` |
|
|
423
|
+
| `CreatedByMixin` | `created_by` |
|
|
424
|
+
| `UpdatedByMixin` | `updated_by` |
|
|
425
|
+
| `AuditMixin` | `created_by` + `updated_by` |
|
|
426
|
+
| `SoftDeleteMixin` | `is_deleted` + `deleted_at` |
|
|
408
427
|
| `SoftDeleteAuditMixin` | `is_deleted` + `deleted_at` + `deleted_by` |
|
|
409
428
|
|
|
410
429
|
```python
|
|
411
|
-
from fastapi_augment.db.sqlalchemy import ModelBase
|
|
430
|
+
from fastapi_augment.db.sqlalchemy import ModelBase
|
|
431
|
+
from fastapi_augment.db.sqlalchemy.mixins import TimestampMixin, SoftDeleteMixin
|
|
412
432
|
|
|
413
433
|
class User(TimestampMixin, SoftDeleteMixin, ModelBase):
|
|
414
434
|
__tablename__ = 'users'
|
|
@@ -504,19 +524,20 @@ set_log_level('info')
|
|
|
504
524
|
|
|
505
525
|
**支持的轮转粒度:**
|
|
506
526
|
|
|
507
|
-
| 粒度
|
|
508
|
-
|
|
509
|
-
| `'second'` / `'minute'` / `'hour'` | 每整秒/分/点
|
|
510
|
-
| `'day'`(默认)
|
|
511
|
-
| `'week'`
|
|
512
|
-
| `'month'`
|
|
513
|
-
| `'year'`
|
|
527
|
+
| 粒度 | 说明 |
|
|
528
|
+
| ---------------------------------- | -------------------- |
|
|
529
|
+
| `'second'` / `'minute'` / `'hour'` | 每整秒/分/点 |
|
|
530
|
+
| `'day'`(默认) | 每天 00:00 |
|
|
531
|
+
| `'week'` | 每周一 00:00 |
|
|
532
|
+
| `'month'` | 每月 1 日 00:00 |
|
|
533
|
+
| `'year'` | 每年 1 月 1 日 00:00 |
|
|
514
534
|
|
|
515
535
|
**核心能力:**
|
|
516
536
|
|
|
517
537
|
- **request_id 注入** — 每条日志自动携带当前请求的 `request_id`,方便链路追踪
|
|
518
538
|
- **uvicorn 接管** — 统一 `uvicorn.error` / `uvicorn.access` 的日志名称为 `uvicorn`,屏蔽第三方库 DEBUG 噪声
|
|
519
|
-
- **多进程安全** — 日志轮转时捕获 `PermissionError
|
|
539
|
+
- **多进程安全** — 日志轮转时捕获 `OSError`(含 `PermissionError`、`FileNotFoundError`),兼容多进程部署(如 `uvicorn --workers N`)
|
|
540
|
+
- **幂等初始化** — 重复导入或多次调用 `setup_logger()` 不会叠加处理器或工厂链
|
|
520
541
|
- **控制台开关** — `enable_console=False` 可关闭控制台输出,仅保留文件日志
|
|
521
542
|
|
|
522
543
|
### 健康检查 — `health`
|
|
@@ -537,11 +558,20 @@ app = create_app(
|
|
|
537
558
|
|
|
538
559
|
```json
|
|
539
560
|
{
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
561
|
+
"status": "healthy",
|
|
562
|
+
"checks": [
|
|
563
|
+
{
|
|
564
|
+
"name": "app",
|
|
565
|
+
"status": "healthy",
|
|
566
|
+
"latencyMs": 0,
|
|
567
|
+
"details": {
|
|
568
|
+
"status": "running",
|
|
569
|
+
"version": "1.0.0",
|
|
570
|
+
"uptimeSeconds": 3600
|
|
571
|
+
}
|
|
572
|
+
},
|
|
573
|
+
{ "name": "database", "status": "healthy", "latencyMs": 2.3 }
|
|
574
|
+
]
|
|
545
575
|
}
|
|
546
576
|
```
|
|
547
577
|
|
|
@@ -623,7 +653,7 @@ fastapi_augment/
|
|
|
623
653
|
│ ├── engine.py # EngineManager / NodeConfig / ClusterTopology
|
|
624
654
|
│ ├── session.py # SessionFactory(读写分离)
|
|
625
655
|
│ ├── model_base.py # ModelBase(ULID 主键)
|
|
626
|
-
│ ├──
|
|
656
|
+
│ ├── repository_base.py # RepositoryBase(泛型仓储 + paginate)
|
|
627
657
|
│ ├── migrate.py # 数据库迁移 CLI
|
|
628
658
|
│ ├── migrations/ # Alembic 迁移环境(env.py / script.py.mako)
|
|
629
659
|
│ └── mixins/ # Timestamp / Audit / SoftDelete
|
|
@@ -6,13 +6,13 @@
|
|
|
6
6
|
|
|
7
7
|
- **应用工厂** — 一行代码创建 FastAPI 实例,自动装配中间件、路由、生命周期与数据库
|
|
8
8
|
- **生命周期管理** — 多注册表、优先级、超时控制、异常策略的启动/关闭钩子
|
|
9
|
-
- **读写分离** — 单库 / 主从 /
|
|
10
|
-
-
|
|
9
|
+
- **读写分离** — 单库 / 主从 / 集群拓扑的异步引擎管理,线程安全的 Session 自动路由
|
|
10
|
+
- **泛型仓储** — 类型安全的异步 Repository,支持直接实例化与子类继承两种方式
|
|
11
11
|
- **可组合 Mixin** — 时间戳、审计、软删除等列混入,自由组合
|
|
12
12
|
- **统一响应** — 全局 `APIResponse` 格式,自动追踪 `request_id`
|
|
13
13
|
- **HTTP 异常** — 完整的 4xx 异常子类,内置默认文案
|
|
14
14
|
- **OpenAPI 优化** — 自动清理 422 响应、可选 Bearer 认证
|
|
15
|
-
- **日志管理** — request_id 自动注入、uvicorn
|
|
15
|
+
- **日志管理** — request_id 自动注入、uvicorn 接管、多进程安全轮转、幂等初始化、一键配置
|
|
16
16
|
- **健康检查** — 可扩展的检查器模式,内置应用状态与数据库连通性检查,一行开关
|
|
17
17
|
- **配置管理** — 基于 pydantic-settings,支持 `.env` 文件、环境变量前缀、嵌套配置
|
|
18
18
|
- **数据库迁移 CLI** — 一行命令生成/执行迁移,自动发现用户模型
|
|
@@ -41,8 +41,9 @@ from fastapi import APIRouter
|
|
|
41
41
|
from fastapi_augment import create_app
|
|
42
42
|
from fastapi_augment.db.sqlalchemy import (
|
|
43
43
|
ClusterTopology, NodeConfig, EngineManager, SessionFactory,
|
|
44
|
-
ModelBase,
|
|
44
|
+
ModelBase, RepositoryBase,
|
|
45
45
|
)
|
|
46
|
+
from fastapi_augment.db.sqlalchemy.mixins import TimestampMixin
|
|
46
47
|
from fastapi_augment.schemas import response_success
|
|
47
48
|
|
|
48
49
|
# ── 1. 数据库拓扑 ──────────────────────────────────────
|
|
@@ -61,13 +62,13 @@ class User(TimestampMixin, ModelBase):
|
|
|
61
62
|
|
|
62
63
|
# ── 3. 路由 ────────────────────────────────────────────
|
|
63
64
|
router = APIRouter()
|
|
64
|
-
|
|
65
|
+
user_repo = RepositoryBase(User)
|
|
65
66
|
|
|
66
67
|
|
|
67
68
|
@router.get('/users')
|
|
68
69
|
async def list_users():
|
|
69
70
|
async with sessions.read_session() as session:
|
|
70
|
-
users = await
|
|
71
|
+
users = await user_repo.list(session, is_active=True, limit=10)
|
|
71
72
|
return response_success(data=users)
|
|
72
73
|
|
|
73
74
|
|
|
@@ -87,14 +88,14 @@ app = create_app(
|
|
|
87
88
|
|
|
88
89
|
统一创建 FastAPI 实例,自动装配以下组件:
|
|
89
90
|
|
|
90
|
-
| 组件
|
|
91
|
-
|
|
92
|
-
| 生命周期 | 接入 `fastapi_lifespan`,合并用户注册表与 `core_registry`
|
|
93
|
-
| 中间件
|
|
94
|
-
| 路由
|
|
95
|
-
| OpenAPI
|
|
96
|
-
| 数据库
|
|
97
|
-
| 健康检查 | `health_check=True` 一键启用 `/health` 端点
|
|
91
|
+
| 组件 | 说明 |
|
|
92
|
+
| -------- | ---------------------------------------------------------- |
|
|
93
|
+
| 生命周期 | 接入 `fastapi_lifespan`,合并用户注册表与 `core_registry` |
|
|
94
|
+
| 中间件 | 自动添加 `RequestIdMiddleware`,可选 CORS |
|
|
95
|
+
| 路由 | 支持 `APIRouter` 列表或 `(router, kwargs)` 元组 |
|
|
96
|
+
| OpenAPI | 自动清理 422 响应、可选 Bearer 认证 |
|
|
97
|
+
| 数据库 | 可选挂载 `EngineManager` / `SessionFactory` 到 `app.state` |
|
|
98
|
+
| 健康检查 | `health_check=True` 一键启用 `/health` 端点 |
|
|
98
99
|
|
|
99
100
|
```python
|
|
100
101
|
from fastapi_augment import create_app, HookRegistry
|
|
@@ -107,7 +108,7 @@ async def init_cache() -> None:
|
|
|
107
108
|
|
|
108
109
|
app = create_app(
|
|
109
110
|
title='My Service',
|
|
110
|
-
registries=
|
|
111
|
+
registries=registry, # 单个或列表均可
|
|
111
112
|
cors_allow_origins=['*'],
|
|
112
113
|
openapi_enable_bearer_auth=True,
|
|
113
114
|
health_check=True, # 启用健康检查
|
|
@@ -166,7 +167,7 @@ topology = ClusterTopology(
|
|
|
166
167
|
)
|
|
167
168
|
|
|
168
169
|
manager = EngineManager(topology).start()
|
|
169
|
-
# 读引擎轮询(round-robin
|
|
170
|
+
# 读引擎轮询(round-robin),线程安全
|
|
170
171
|
read_engine = manager.next_read_engine()
|
|
171
172
|
```
|
|
172
173
|
|
|
@@ -197,6 +198,8 @@ async def list_users(session: AsyncSession = Depends(sessions.depends_read)):
|
|
|
197
198
|
...
|
|
198
199
|
```
|
|
199
200
|
|
|
201
|
+
> **缓存说明:** `read_session()` 按 `AsyncEngine` 缓存 `async_sessionmaker`,线程安全,避免重复创建。打印 `SessionFactory` 实例可查看已缓存的引擎信息。
|
|
202
|
+
|
|
200
203
|
#### 模型基类 — `ModelBase`
|
|
201
204
|
|
|
202
205
|
基于 ULID 主键的声明式模型基类:
|
|
@@ -209,61 +212,74 @@ class User(TimestampMixin, ModelBase):
|
|
|
209
212
|
name: str
|
|
210
213
|
```
|
|
211
214
|
|
|
212
|
-
####
|
|
215
|
+
#### 泛型仓储 — `RepositoryBase`
|
|
213
216
|
|
|
214
|
-
|
|
217
|
+
类型安全的异步仓储基类,Repository 方法只 **flush**,不 commit,事务边界由调用方控制。
|
|
218
|
+
支持两种使用方式:
|
|
215
219
|
|
|
216
220
|
```python
|
|
217
|
-
from fastapi_augment.db.sqlalchemy import
|
|
221
|
+
from fastapi_augment.db.sqlalchemy import RepositoryBase
|
|
222
|
+
|
|
223
|
+
# 方式 1:直接实例化 — 显式传入模型类
|
|
224
|
+
user_repo = RepositoryBase(User)
|
|
225
|
+
|
|
226
|
+
# 方式 2:子类继承 — 通过泛型参数绑定模型,可扩展自定义方法
|
|
227
|
+
class UserRepo(RepositoryBase[User]):
|
|
228
|
+
async def find_by_email(self, session, email: str) -> User | None:
|
|
229
|
+
return await self.get_one(session, email=email)
|
|
218
230
|
|
|
219
|
-
|
|
231
|
+
user_repo = UserRepo() # 无需再传 User
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
**CRUD 操作:**
|
|
220
235
|
|
|
236
|
+
```python
|
|
221
237
|
# Create(静态方法)
|
|
222
238
|
async with sessions.transaction() as session:
|
|
223
|
-
await
|
|
224
|
-
await
|
|
239
|
+
await user_repo.create(session, User(name='alice'))
|
|
240
|
+
await user_repo.create_many(session, [User(name='bob'), User(name='carol')])
|
|
225
241
|
|
|
226
242
|
# Read(实例方法)
|
|
227
243
|
async with sessions.read_session() as session:
|
|
228
|
-
user = await
|
|
229
|
-
user = await
|
|
230
|
-
users = await
|
|
231
|
-
total = await
|
|
232
|
-
has_admin = await
|
|
244
|
+
user = await user_repo.get(session, id_='01HXK...')
|
|
245
|
+
user = await user_repo.get_one(session, name='alice')
|
|
246
|
+
users = await user_repo.list(session, role='admin', order_by=['-created_at'], limit=10)
|
|
247
|
+
total = await user_repo.count(session, is_active=True)
|
|
248
|
+
has_admin = await user_repo.exists(session, role='admin')
|
|
233
249
|
|
|
234
250
|
# 分页查询(返回 dict:items / page / size / total / pages)
|
|
235
|
-
result = await
|
|
251
|
+
result = await user_repo.paginate(session, page=1, size=10, role='admin', order_by=['-created_at'])
|
|
236
252
|
# result = {'items': [...], 'page': 1, 'size': 10, 'total': 100, 'pages': 10}
|
|
237
253
|
|
|
238
254
|
# Update
|
|
239
255
|
async with sessions.transaction() as session:
|
|
240
|
-
await
|
|
241
|
-
affected = await
|
|
256
|
+
await user_repo.update(session, user, name='new_name')
|
|
257
|
+
affected = await user_repo.update_by_id(session, id_='01HXK...', name='new_name')
|
|
242
258
|
|
|
243
259
|
# Delete
|
|
244
260
|
async with sessions.transaction() as session:
|
|
245
|
-
await
|
|
246
|
-
deleted = await
|
|
247
|
-
count = await
|
|
261
|
+
await user_repo.delete(session, user)
|
|
262
|
+
deleted = await user_repo.delete_by_id(session, id_='01HXK...')
|
|
263
|
+
count = await user_repo.delete_where(session, is_active=False)
|
|
248
264
|
```
|
|
249
265
|
|
|
250
266
|
**过滤语法:**
|
|
251
267
|
|
|
252
268
|
```python
|
|
253
269
|
# 关键字过滤 — 等值匹配
|
|
254
|
-
await
|
|
270
|
+
await user_repo.list(session, name='alice')
|
|
255
271
|
|
|
256
272
|
# 序列 — 自动转为 IN 查询
|
|
257
|
-
await
|
|
273
|
+
await user_repo.list(session, id_=['01HXK...', '01HXL...'])
|
|
258
274
|
|
|
259
275
|
# None — 自动转为 IS NULL
|
|
260
|
-
await
|
|
276
|
+
await user_repo.list(session, deleted_at=None)
|
|
261
277
|
|
|
262
278
|
# 原生 SQLAlchemy 表达式
|
|
263
|
-
await
|
|
279
|
+
await user_repo.list(session, expressions=(User.age > 18,))
|
|
264
280
|
|
|
265
281
|
# 排序:字段名前缀 - 表示降序
|
|
266
|
-
await
|
|
282
|
+
await user_repo.list(session, order_by=['-created_at', 'name'])
|
|
267
283
|
```
|
|
268
284
|
|
|
269
285
|
### 数据库迁移 CLI — `fastapi-augment-migrate`
|
|
@@ -347,34 +363,35 @@ fastapi-augment-migrate upgrade --project-dir /path/to/project
|
|
|
347
363
|
|
|
348
364
|
#### CLI 参数一览
|
|
349
365
|
|
|
350
|
-
| 子命令
|
|
351
|
-
|
|
352
|
-
| `init`
|
|
353
|
-
|
|
|
354
|
-
| `generate` | `--message`
|
|
355
|
-
|
|
|
356
|
-
|
|
|
357
|
-
| `upgrade`
|
|
358
|
-
|
|
|
359
|
-
|
|
|
360
|
-
|
|
|
366
|
+
| 子命令 | 参数 | 说明 |
|
|
367
|
+
| ---------- | --------------- | --------------------------------------- |
|
|
368
|
+
| `init` | `--db-url` | 数据库 URL(默认 `sqlite:///app.db`) |
|
|
369
|
+
| | `--project-dir` | 项目根目录(默认当前目录) |
|
|
370
|
+
| `generate` | `--message` | **必填**,迁移描述 |
|
|
371
|
+
| | `--models` | **必填**,模型模块路径,逗号分隔 |
|
|
372
|
+
| | `--project-dir` | 项目根目录(默认当前目录) |
|
|
373
|
+
| `upgrade` | `--db-url` | 数据库 URL(不传则从 alembic.ini 读取) |
|
|
374
|
+
| | `--revision` | 目标版本(默认 `head`) |
|
|
375
|
+
| | `--downgrade` | 降级模式 |
|
|
376
|
+
| | `--project-dir` | 项目根目录(默认当前目录) |
|
|
361
377
|
|
|
362
378
|
### 模型 Mixin — `db.sqlalchemy.mixins`
|
|
363
379
|
|
|
364
380
|
可组合的列混入,按需叠加:
|
|
365
381
|
|
|
366
|
-
| Mixin
|
|
367
|
-
|
|
368
|
-
| `CreatedAtMixin`
|
|
369
|
-
| `TimestampMixin`
|
|
370
|
-
| `CreatedByMixin`
|
|
371
|
-
| `UpdatedByMixin`
|
|
372
|
-
| `AuditMixin`
|
|
373
|
-
| `SoftDeleteMixin`
|
|
382
|
+
| Mixin | 提供的列 |
|
|
383
|
+
| ---------------------- | ------------------------------------------ |
|
|
384
|
+
| `CreatedAtMixin` | `created_at` |
|
|
385
|
+
| `TimestampMixin` | `created_at` + `updated_at` |
|
|
386
|
+
| `CreatedByMixin` | `created_by` |
|
|
387
|
+
| `UpdatedByMixin` | `updated_by` |
|
|
388
|
+
| `AuditMixin` | `created_by` + `updated_by` |
|
|
389
|
+
| `SoftDeleteMixin` | `is_deleted` + `deleted_at` |
|
|
374
390
|
| `SoftDeleteAuditMixin` | `is_deleted` + `deleted_at` + `deleted_by` |
|
|
375
391
|
|
|
376
392
|
```python
|
|
377
|
-
from fastapi_augment.db.sqlalchemy import ModelBase
|
|
393
|
+
from fastapi_augment.db.sqlalchemy import ModelBase
|
|
394
|
+
from fastapi_augment.db.sqlalchemy.mixins import TimestampMixin, SoftDeleteMixin
|
|
378
395
|
|
|
379
396
|
class User(TimestampMixin, SoftDeleteMixin, ModelBase):
|
|
380
397
|
__tablename__ = 'users'
|
|
@@ -470,19 +487,20 @@ set_log_level('info')
|
|
|
470
487
|
|
|
471
488
|
**支持的轮转粒度:**
|
|
472
489
|
|
|
473
|
-
| 粒度
|
|
474
|
-
|
|
475
|
-
| `'second'` / `'minute'` / `'hour'` | 每整秒/分/点
|
|
476
|
-
| `'day'`(默认)
|
|
477
|
-
| `'week'`
|
|
478
|
-
| `'month'`
|
|
479
|
-
| `'year'`
|
|
490
|
+
| 粒度 | 说明 |
|
|
491
|
+
| ---------------------------------- | -------------------- |
|
|
492
|
+
| `'second'` / `'minute'` / `'hour'` | 每整秒/分/点 |
|
|
493
|
+
| `'day'`(默认) | 每天 00:00 |
|
|
494
|
+
| `'week'` | 每周一 00:00 |
|
|
495
|
+
| `'month'` | 每月 1 日 00:00 |
|
|
496
|
+
| `'year'` | 每年 1 月 1 日 00:00 |
|
|
480
497
|
|
|
481
498
|
**核心能力:**
|
|
482
499
|
|
|
483
500
|
- **request_id 注入** — 每条日志自动携带当前请求的 `request_id`,方便链路追踪
|
|
484
501
|
- **uvicorn 接管** — 统一 `uvicorn.error` / `uvicorn.access` 的日志名称为 `uvicorn`,屏蔽第三方库 DEBUG 噪声
|
|
485
|
-
- **多进程安全** — 日志轮转时捕获 `PermissionError
|
|
502
|
+
- **多进程安全** — 日志轮转时捕获 `OSError`(含 `PermissionError`、`FileNotFoundError`),兼容多进程部署(如 `uvicorn --workers N`)
|
|
503
|
+
- **幂等初始化** — 重复导入或多次调用 `setup_logger()` 不会叠加处理器或工厂链
|
|
486
504
|
- **控制台开关** — `enable_console=False` 可关闭控制台输出,仅保留文件日志
|
|
487
505
|
|
|
488
506
|
### 健康检查 — `health`
|
|
@@ -503,11 +521,20 @@ app = create_app(
|
|
|
503
521
|
|
|
504
522
|
```json
|
|
505
523
|
{
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
524
|
+
"status": "healthy",
|
|
525
|
+
"checks": [
|
|
526
|
+
{
|
|
527
|
+
"name": "app",
|
|
528
|
+
"status": "healthy",
|
|
529
|
+
"latencyMs": 0,
|
|
530
|
+
"details": {
|
|
531
|
+
"status": "running",
|
|
532
|
+
"version": "1.0.0",
|
|
533
|
+
"uptimeSeconds": 3600
|
|
534
|
+
}
|
|
535
|
+
},
|
|
536
|
+
{ "name": "database", "status": "healthy", "latencyMs": 2.3 }
|
|
537
|
+
]
|
|
511
538
|
}
|
|
512
539
|
```
|
|
513
540
|
|
|
@@ -589,7 +616,7 @@ fastapi_augment/
|
|
|
589
616
|
│ ├── engine.py # EngineManager / NodeConfig / ClusterTopology
|
|
590
617
|
│ ├── session.py # SessionFactory(读写分离)
|
|
591
618
|
│ ├── model_base.py # ModelBase(ULID 主键)
|
|
592
|
-
│ ├──
|
|
619
|
+
│ ├── repository_base.py # RepositoryBase(泛型仓储 + paginate)
|
|
593
620
|
│ ├── migrate.py # 数据库迁移 CLI
|
|
594
621
|
│ ├── migrations/ # Alembic 迁移环境(env.py / script.py.mako)
|
|
595
622
|
│ └── mixins/ # Timestamp / Audit / SoftDelete
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.1.2
|