fastapi-augment 0.1.2__tar.gz → 0.1.4__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.2 → fastapi_augment-0.1.4}/PKG-INFO +117 -22
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/README.md +116 -21
- fastapi_augment-0.1.4/VERSION +1 -0
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/src/fastapi_augment/common/constants.py +2 -1
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/src/fastapi_augment/common/exception_handlers.py +10 -10
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/src/fastapi_augment/common/exceptions.py +18 -18
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/src/fastapi_augment/common/utils/__init__.py +1 -1
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/src/fastapi_augment/common/utils/paths.py +3 -3
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/src/fastapi_augment/common/utils/strings.py +17 -4
- fastapi_augment-0.1.4/src/fastapi_augment/config/__init__.py +12 -0
- fastapi_augment-0.1.4/src/fastapi_augment/config/base_settings.py +253 -0
- fastapi_augment-0.1.4/src/fastapi_augment/config/database_settings.py +219 -0
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/src/fastapi_augment/db/sqlalchemy/__init__.py +12 -0
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/src/fastapi_augment/db/sqlalchemy/alembic/env.py +1 -1
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/src/fastapi_augment/db/sqlalchemy/engine.py +9 -2
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/src/fastapi_augment/db/sqlalchemy/migrate.py +7 -7
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/src/fastapi_augment/db/sqlalchemy/mixins/__init__.py +1 -1
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/src/fastapi_augment/db/sqlalchemy/mixins/audit.py +3 -3
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/src/fastapi_augment/db/sqlalchemy/mixins/soft_delete.py +4 -4
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/src/fastapi_augment/db/sqlalchemy/mixins/timestamp.py +3 -3
- fastapi_augment-0.1.4/src/fastapi_augment/db/sqlalchemy/query_parser.py +447 -0
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/src/fastapi_augment/db/sqlalchemy/repository_base.py +37 -6
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/src/fastapi_augment/db/sqlalchemy/session.py +32 -23
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/src/fastapi_augment/factory.py +20 -12
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/src/fastapi_augment/health/__init__.py +6 -6
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/src/fastapi_augment/health/checker.py +13 -9
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/src/fastapi_augment/health/checkers.py +38 -21
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/src/fastapi_augment/health/router.py +13 -8
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/src/fastapi_augment/lifespan.py +23 -21
- {fastapi_augment-0.1.2/src/fastapi_augment/log → fastapi_augment-0.1.4/src/fastapi_augment/logger}/__init__.py +9 -9
- {fastapi_augment-0.1.2/src/fastapi_augment/log → fastapi_augment-0.1.4/src/fastapi_augment/logger}/filters.py +3 -3
- {fastapi_augment-0.1.2/src/fastapi_augment/log → fastapi_augment-0.1.4/src/fastapi_augment/logger}/handlers.py +14 -15
- fastapi_augment-0.1.2/src/fastapi_augment/log/factory.py → fastapi_augment-0.1.4/src/fastapi_augment/logger/record_factory.py +5 -5
- fastapi_augment-0.1.2/src/fastapi_augment/log/config.py → fastapi_augment-0.1.4/src/fastapi_augment/logger/setup.py +17 -9
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/src/fastapi_augment/middlewares/base.py +4 -4
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/src/fastapi_augment/middlewares/request_id.py +3 -3
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/src/fastapi_augment/openapi.py +6 -22
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/src/fastapi_augment.egg-info/PKG-INFO +117 -22
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/src/fastapi_augment.egg-info/SOURCES.txt +9 -6
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/tests/test_config.py +23 -23
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/tests/test_db_repository.py +429 -412
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/tests/test_factory.py +2 -2
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/tests/test_log.py +21 -21
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/tests/test_openapi.py +0 -31
- fastapi_augment-0.1.4/tests/test_query_parser.py +246 -0
- fastapi_augment-0.1.4/tests/test_settings.py +128 -0
- fastapi_augment-0.1.2/VERSION +0 -1
- fastapi_augment-0.1.2/src/fastapi_augment/config/__init__.py +0 -8
- fastapi_augment-0.1.2/src/fastapi_augment/config/settings.py +0 -104
- fastapi_augment-0.1.2/tests/test_settings.py +0 -78
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/pyproject.toml +0 -0
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/setup.cfg +0 -0
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/src/fastapi_augment/__init__.py +0 -0
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/src/fastapi_augment/common/__init__.py +0 -0
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/src/fastapi_augment/db/__init__.py +0 -0
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/src/fastapi_augment/db/sqlalchemy/alembic/README +0 -0
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/src/fastapi_augment/db/sqlalchemy/alembic/__init__.py +0 -0
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/src/fastapi_augment/db/sqlalchemy/alembic/script.py.mako +0 -0
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/src/fastapi_augment/db/sqlalchemy/base.py +0 -0
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/src/fastapi_augment/db/sqlalchemy/model_base.py +0 -0
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/src/fastapi_augment/middlewares/__init__.py +0 -0
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/src/fastapi_augment/py.typed +0 -0
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/src/fastapi_augment/schemas/__init__.py +0 -0
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/src/fastapi_augment/schemas/base.py +0 -0
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/src/fastapi_augment/schemas/pagination.py +0 -0
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/src/fastapi_augment/schemas/request.py +0 -0
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/src/fastapi_augment/schemas/response.py +0 -0
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/src/fastapi_augment/schemas/types.py +0 -0
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/src/fastapi_augment.egg-info/dependency_links.txt +0 -0
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/src/fastapi_augment.egg-info/entry_points.txt +0 -0
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/src/fastapi_augment.egg-info/requires.txt +0 -0
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/src/fastapi_augment.egg-info/top_level.txt +0 -0
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/tests/test_constants.py +0 -0
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/tests/test_db_engine.py +0 -0
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/tests/test_db_session_models.py +0 -0
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/tests/test_exception_handlers.py +0 -0
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/tests/test_exceptions.py +0 -0
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/tests/test_health.py +0 -0
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/tests/test_lifespan.py +0 -0
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/tests/test_middlewares.py +0 -0
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/tests/test_migrate.py +0 -0
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/tests/test_model_base.py +0 -0
- {fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/tests/test_schemas.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fastapi-augment
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
4
4
|
Summary: FastAPI 通用代码工具包,跨项目复用
|
|
5
5
|
Author-email: zarkhan <hanguangzheng@qq.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -45,10 +45,11 @@ Requires-Dist: fastapi-augment[config,orjson,sqlalchemy,uvicorn]; extra == "stan
|
|
|
45
45
|
- **生命周期管理** — 多注册表、优先级、超时控制、异常策略的启动/关闭钩子
|
|
46
46
|
- **读写分离** — 单库 / 主从 / 集群拓扑的异步引擎管理,线程安全的 Session 自动路由
|
|
47
47
|
- **泛型仓储** — 类型安全的异步 Repository,支持直接实例化与子类继承两种方式
|
|
48
|
+
- **查询解析器** — REST 风格 query string 转 SQLAlchemy 表达式,支持 FIQL 条件、关键字搜索、排序
|
|
48
49
|
- **可组合 Mixin** — 时间戳、审计、软删除等列混入,自由组合
|
|
49
50
|
- **统一响应** — 全局 `APIResponse` 格式,自动追踪 `request_id`
|
|
50
51
|
- **HTTP 异常** — 完整的 4xx 异常子类,内置默认文案
|
|
51
|
-
- **OpenAPI 优化** — 自动清理 422
|
|
52
|
+
- **OpenAPI 优化** — 自动清理 422 响应与验证错误模型
|
|
52
53
|
- **日志管理** — request_id 自动注入、uvicorn 接管、多进程安全轮转、幂等初始化、一键配置
|
|
53
54
|
- **健康检查** — 可扩展的检查器模式,内置应用状态与数据库连通性检查,一行开关
|
|
54
55
|
- **配置管理** — 基于 pydantic-settings,支持 `.env` 文件、环境变量前缀、嵌套配置
|
|
@@ -130,7 +131,7 @@ app = create_app(
|
|
|
130
131
|
| 生命周期 | 接入 `fastapi_lifespan`,合并用户注册表与 `core_registry` |
|
|
131
132
|
| 中间件 | 自动添加 `RequestIdMiddleware`,可选 CORS |
|
|
132
133
|
| 路由 | 支持 `APIRouter` 列表或 `(router, kwargs)` 元组 |
|
|
133
|
-
| OpenAPI | 自动清理 422
|
|
134
|
+
| OpenAPI | 自动清理 422 响应与验证错误模型 |
|
|
134
135
|
| 数据库 | 可选挂载 `EngineManager` / `SessionFactory` 到 `app.state` |
|
|
135
136
|
| 健康检查 | `health_check=True` 一键启用 `/health` 端点 |
|
|
136
137
|
|
|
@@ -147,7 +148,6 @@ app = create_app(
|
|
|
147
148
|
title='My Service',
|
|
148
149
|
registries=registry, # 单个或列表均可
|
|
149
150
|
cors_allow_origins=['*'],
|
|
150
|
-
openapi_enable_bearer_auth=True,
|
|
151
151
|
health_check=True, # 启用健康检查
|
|
152
152
|
)
|
|
153
153
|
```
|
|
@@ -260,10 +260,12 @@ from fastapi_augment.db.sqlalchemy import RepositoryBase
|
|
|
260
260
|
# 方式 1:直接实例化 — 显式传入模型类
|
|
261
261
|
user_repo = RepositoryBase(User)
|
|
262
262
|
|
|
263
|
+
|
|
263
264
|
# 方式 2:子类继承 — 通过泛型参数绑定模型,可扩展自定义方法
|
|
264
265
|
class UserRepo(RepositoryBase[User]):
|
|
265
266
|
async def find_by_email(self, session, email: str) -> User | None:
|
|
266
|
-
return await self.
|
|
267
|
+
return await self.get_first(session, email=email)
|
|
268
|
+
|
|
267
269
|
|
|
268
270
|
user_repo = UserRepo() # 无需再传 User
|
|
269
271
|
```
|
|
@@ -279,7 +281,8 @@ async with sessions.transaction() as session:
|
|
|
279
281
|
# Read(实例方法)
|
|
280
282
|
async with sessions.read_session() as session:
|
|
281
283
|
user = await user_repo.get(session, id_='01HXK...')
|
|
282
|
-
user = await user_repo.
|
|
284
|
+
user = await user_repo.get_first(session, name='alice') # 取第一条,无匹配返回 None
|
|
285
|
+
user = await user_repo.get_unique(session, email='a@b.com') # 精确唯一,多条匹配抛 MultipleResultsFound
|
|
283
286
|
users = await user_repo.list(session, role='admin', order_by=['-created_at'], limit=10)
|
|
284
287
|
total = await user_repo.count(session, is_active=True)
|
|
285
288
|
has_admin = await user_repo.exists(session, role='admin')
|
|
@@ -319,6 +322,92 @@ await user_repo.list(session, expressions=(User.age > 18,))
|
|
|
319
322
|
await user_repo.list(session, order_by=['-created_at', 'name'])
|
|
320
323
|
```
|
|
321
324
|
|
|
325
|
+
### 查询解析器 — `query_parser`
|
|
326
|
+
|
|
327
|
+
将 REST 风格的 query string 转为 SQLAlchemy `ColumnElement` 条件表达式,可直接传入 `RepositoryBase` 的 `expressions` / `order_by` 参数。
|
|
328
|
+
|
|
329
|
+
#### where 组合条件(FIQL 风格)
|
|
330
|
+
|
|
331
|
+
```
|
|
332
|
+
where = and_group ("," and_group)* , 表示 OR
|
|
333
|
+
and_group = unit (";" unit)* ; 表示 AND
|
|
334
|
+
unit = "(" where ")" | condition
|
|
335
|
+
condition = field OP value
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
**支持的操作符:**
|
|
339
|
+
|
|
340
|
+
| 语法 | 含义 | 示例 |
|
|
341
|
+
| ---------------- | -------------------- | ------------------------- |
|
|
342
|
+
| `field==value` | 等于 | `status==1` |
|
|
343
|
+
| `field!=value` | 不等 | `status!=0` |
|
|
344
|
+
| `field~=value` | 模糊包含 (ILIKE) | `name~=张` |
|
|
345
|
+
| `field>value` | 大于 | `age>18` |
|
|
346
|
+
| `field>=value` | 大于等于 | `age>=18` |
|
|
347
|
+
| `field<value` | 小于 | `age<60` |
|
|
348
|
+
| `field<=value` | 小于等于 | `age<=60` |
|
|
349
|
+
| `field~start~end`| 区间 (BETWEEN) | `age~18~60` |
|
|
350
|
+
|
|
351
|
+
```python
|
|
352
|
+
from fastapi_augment.db.sqlalchemy import parse_where
|
|
353
|
+
|
|
354
|
+
# 昵称含张 且 状态非禁用
|
|
355
|
+
expr = parse_where('nickname~=张;status!=0', User)
|
|
356
|
+
|
|
357
|
+
# 括号内 OR,与区间 AND
|
|
358
|
+
expr = parse_where('(nickname~=张,username~=王);age~20~30', User)
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
#### lookup 精确匹配
|
|
362
|
+
|
|
363
|
+
```python
|
|
364
|
+
from fastapi_augment.db.sqlalchemy import parse_lookup
|
|
365
|
+
|
|
366
|
+
# 单字段精确匹配
|
|
367
|
+
expr = parse_lookup('phone==13800138000', User, fields={'phone', 'email'})
|
|
368
|
+
|
|
369
|
+
# 多字段 AND(; 分隔)
|
|
370
|
+
expr = parse_lookup('phone==138;status==1', User, fields={'phone', 'status'})
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
#### 关键字搜索
|
|
374
|
+
|
|
375
|
+
```python
|
|
376
|
+
from fastapi_augment.db.sqlalchemy import parse_keyword
|
|
377
|
+
|
|
378
|
+
# 多字段 OR 模糊搜索
|
|
379
|
+
expr = parse_keyword('admin', 'username,email,nickname', User)
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
#### 排序
|
|
383
|
+
|
|
384
|
+
```python
|
|
385
|
+
from fastapi_augment.db.sqlalchemy import parse_sort
|
|
386
|
+
|
|
387
|
+
# - 前缀表示降序,无前缀为升序
|
|
388
|
+
order_by = parse_sort('-created_at,nickname', User)
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
#### 一键组合 — `build_query_expressions`
|
|
392
|
+
|
|
393
|
+
```python
|
|
394
|
+
from fastapi_augment.db.sqlalchemy import build_query_expressions
|
|
395
|
+
|
|
396
|
+
expressions, order_by = build_query_expressions(
|
|
397
|
+
User,
|
|
398
|
+
where='status!=0;age>=18',
|
|
399
|
+
q='admin',
|
|
400
|
+
q_field='username,nickname',
|
|
401
|
+
sort='-created_at',
|
|
402
|
+
)
|
|
403
|
+
|
|
404
|
+
# 直接传给 paginate / list
|
|
405
|
+
result = await user_repo.paginate(
|
|
406
|
+
session, page=1, size=10,
|
|
407
|
+
expressions=expressions, order_by=order_by,
|
|
408
|
+
)
|
|
409
|
+
```
|
|
410
|
+
|
|
322
411
|
### 数据库迁移 CLI — `fastapi-augment-migrate`
|
|
323
412
|
|
|
324
413
|
内置 Alembic 迁移工具,提供 `init` / `generate` / `upgrade` 三个子命令,开箱即用。
|
|
@@ -508,12 +597,12 @@ raise BadRequestError(detail='用户名不能为空')
|
|
|
508
597
|
raise TooManyRequestsError(retry_after=60)
|
|
509
598
|
```
|
|
510
599
|
|
|
511
|
-
### 日志管理 — `
|
|
600
|
+
### 日志管理 — `logger`
|
|
512
601
|
|
|
513
602
|
导入即生效:自动注入 `request_id` 到每条日志、接管 uvicorn/fastapi 日志输出。
|
|
514
603
|
|
|
515
604
|
```python
|
|
516
|
-
from fastapi_augment.
|
|
605
|
+
from fastapi_augment.logger import setup_logger, set_log_level
|
|
517
606
|
|
|
518
607
|
# 一键配置:控制台 + 按天轮转文件日志
|
|
519
608
|
setup_logger(log_dir='./logs', rotation='day', backup_count=30)
|
|
@@ -602,26 +691,30 @@ app.include_router(create_health_router(
|
|
|
602
691
|
|
|
603
692
|
### 配置管理 — `config`
|
|
604
693
|
|
|
605
|
-
基于 `pydantic-settings
|
|
694
|
+
基于 `pydantic-settings`,支持多种配置来源(环境变量、.env、JSON、YAML、TOML),通过不同类方法加载:
|
|
606
695
|
|
|
607
696
|
```python
|
|
608
|
-
from fastapi_augment.config import
|
|
697
|
+
from fastapi_augment.config import AugmentBaseSettings
|
|
609
698
|
|
|
610
|
-
class Settings(
|
|
699
|
+
class Settings(AugmentBaseSettings):
|
|
611
700
|
database_url: str
|
|
612
701
|
redis_url: str = ''
|
|
613
702
|
debug: bool = False
|
|
614
703
|
secret_key: str = 'change-me'
|
|
615
704
|
|
|
616
|
-
#
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
705
|
+
# 从环境变量加载
|
|
706
|
+
cfg = Settings.from_env(env_prefix='APP_', env_nested_delimiter='__')
|
|
707
|
+
|
|
708
|
+
# 从 .env 文件加载
|
|
709
|
+
cfg = Settings.from_dotenv('.env', env_prefix='APP_')
|
|
710
|
+
|
|
711
|
+
# 从 JSON / YAML / TOML 文件加载
|
|
712
|
+
cfg = Settings.from_json('config.json')
|
|
713
|
+
cfg = Settings.from_yaml('config.yaml')
|
|
714
|
+
cfg = Settings.from_toml('config.toml')
|
|
622
715
|
```
|
|
623
716
|
|
|
624
|
-
支持 `SettingsConfigDict` 的所有参数(`
|
|
717
|
+
支持 `SettingsConfigDict` 的所有参数(`env_prefix`、`secrets_dir`、`yaml_file` 等),
|
|
625
718
|
与模型字段值自动区分,无需关心分类。
|
|
626
719
|
|
|
627
720
|
### 中间件 — `middlewares`
|
|
@@ -647,13 +740,15 @@ fastapi_augment/
|
|
|
647
740
|
│ └── utils/
|
|
648
741
|
│ └── strings.py # 字符串工具 / JSON 序列化
|
|
649
742
|
├── config/
|
|
650
|
-
│
|
|
743
|
+
│ ├── base_settings.py # AugmentBaseSettings 配置管理
|
|
744
|
+
│ └── database_settings.py # DatabaseSettings 数据库配置
|
|
651
745
|
├── db/
|
|
652
746
|
│ └── sqlalchemy/
|
|
653
747
|
│ ├── engine.py # EngineManager / NodeConfig / ClusterTopology
|
|
654
748
|
│ ├── session.py # SessionFactory(读写分离)
|
|
655
749
|
│ ├── model_base.py # ModelBase(ULID 主键)
|
|
656
750
|
│ ├── repository_base.py # RepositoryBase(泛型仓储 + paginate)
|
|
751
|
+
│ ├── query_parser.py # 查询解析器(where / lookup / keyword / sort)
|
|
657
752
|
│ ├── migrate.py # 数据库迁移 CLI
|
|
658
753
|
│ ├── migrations/ # Alembic 迁移环境(env.py / script.py.mako)
|
|
659
754
|
│ └── mixins/ # Timestamp / Audit / SoftDelete
|
|
@@ -661,11 +756,11 @@ fastapi_augment/
|
|
|
661
756
|
│ ├── checker.py # BaseChecker / CheckResult / HealthResponse
|
|
662
757
|
│ ├── checkers.py # AppChecker / DatabaseChecker
|
|
663
758
|
│ └── router.py # create_health_router()
|
|
664
|
-
├──
|
|
665
|
-
│ ├──
|
|
759
|
+
├── logger/
|
|
760
|
+
│ ├── record_factory.py # request_id 注入工厂
|
|
666
761
|
│ ├── filters.py # UvicornNameRewriteFilter
|
|
667
762
|
│ ├── handlers.py # 多进程安全轮转处理器
|
|
668
|
-
│ └──
|
|
763
|
+
│ └── setup.py # setup_logger / set_log_level / set_log_format
|
|
669
764
|
├── middlewares/
|
|
670
765
|
│ ├── base.py # BaseASGIMiddleware
|
|
671
766
|
│ └── request_id.py # RequestId 中间件
|
|
@@ -8,10 +8,11 @@
|
|
|
8
8
|
- **生命周期管理** — 多注册表、优先级、超时控制、异常策略的启动/关闭钩子
|
|
9
9
|
- **读写分离** — 单库 / 主从 / 集群拓扑的异步引擎管理,线程安全的 Session 自动路由
|
|
10
10
|
- **泛型仓储** — 类型安全的异步 Repository,支持直接实例化与子类继承两种方式
|
|
11
|
+
- **查询解析器** — REST 风格 query string 转 SQLAlchemy 表达式,支持 FIQL 条件、关键字搜索、排序
|
|
11
12
|
- **可组合 Mixin** — 时间戳、审计、软删除等列混入,自由组合
|
|
12
13
|
- **统一响应** — 全局 `APIResponse` 格式,自动追踪 `request_id`
|
|
13
14
|
- **HTTP 异常** — 完整的 4xx 异常子类,内置默认文案
|
|
14
|
-
- **OpenAPI 优化** — 自动清理 422
|
|
15
|
+
- **OpenAPI 优化** — 自动清理 422 响应与验证错误模型
|
|
15
16
|
- **日志管理** — request_id 自动注入、uvicorn 接管、多进程安全轮转、幂等初始化、一键配置
|
|
16
17
|
- **健康检查** — 可扩展的检查器模式,内置应用状态与数据库连通性检查,一行开关
|
|
17
18
|
- **配置管理** — 基于 pydantic-settings,支持 `.env` 文件、环境变量前缀、嵌套配置
|
|
@@ -93,7 +94,7 @@ app = create_app(
|
|
|
93
94
|
| 生命周期 | 接入 `fastapi_lifespan`,合并用户注册表与 `core_registry` |
|
|
94
95
|
| 中间件 | 自动添加 `RequestIdMiddleware`,可选 CORS |
|
|
95
96
|
| 路由 | 支持 `APIRouter` 列表或 `(router, kwargs)` 元组 |
|
|
96
|
-
| OpenAPI | 自动清理 422
|
|
97
|
+
| OpenAPI | 自动清理 422 响应与验证错误模型 |
|
|
97
98
|
| 数据库 | 可选挂载 `EngineManager` / `SessionFactory` 到 `app.state` |
|
|
98
99
|
| 健康检查 | `health_check=True` 一键启用 `/health` 端点 |
|
|
99
100
|
|
|
@@ -110,7 +111,6 @@ app = create_app(
|
|
|
110
111
|
title='My Service',
|
|
111
112
|
registries=registry, # 单个或列表均可
|
|
112
113
|
cors_allow_origins=['*'],
|
|
113
|
-
openapi_enable_bearer_auth=True,
|
|
114
114
|
health_check=True, # 启用健康检查
|
|
115
115
|
)
|
|
116
116
|
```
|
|
@@ -223,10 +223,12 @@ from fastapi_augment.db.sqlalchemy import RepositoryBase
|
|
|
223
223
|
# 方式 1:直接实例化 — 显式传入模型类
|
|
224
224
|
user_repo = RepositoryBase(User)
|
|
225
225
|
|
|
226
|
+
|
|
226
227
|
# 方式 2:子类继承 — 通过泛型参数绑定模型,可扩展自定义方法
|
|
227
228
|
class UserRepo(RepositoryBase[User]):
|
|
228
229
|
async def find_by_email(self, session, email: str) -> User | None:
|
|
229
|
-
return await self.
|
|
230
|
+
return await self.get_first(session, email=email)
|
|
231
|
+
|
|
230
232
|
|
|
231
233
|
user_repo = UserRepo() # 无需再传 User
|
|
232
234
|
```
|
|
@@ -242,7 +244,8 @@ async with sessions.transaction() as session:
|
|
|
242
244
|
# Read(实例方法)
|
|
243
245
|
async with sessions.read_session() as session:
|
|
244
246
|
user = await user_repo.get(session, id_='01HXK...')
|
|
245
|
-
user = await user_repo.
|
|
247
|
+
user = await user_repo.get_first(session, name='alice') # 取第一条,无匹配返回 None
|
|
248
|
+
user = await user_repo.get_unique(session, email='a@b.com') # 精确唯一,多条匹配抛 MultipleResultsFound
|
|
246
249
|
users = await user_repo.list(session, role='admin', order_by=['-created_at'], limit=10)
|
|
247
250
|
total = await user_repo.count(session, is_active=True)
|
|
248
251
|
has_admin = await user_repo.exists(session, role='admin')
|
|
@@ -282,6 +285,92 @@ await user_repo.list(session, expressions=(User.age > 18,))
|
|
|
282
285
|
await user_repo.list(session, order_by=['-created_at', 'name'])
|
|
283
286
|
```
|
|
284
287
|
|
|
288
|
+
### 查询解析器 — `query_parser`
|
|
289
|
+
|
|
290
|
+
将 REST 风格的 query string 转为 SQLAlchemy `ColumnElement` 条件表达式,可直接传入 `RepositoryBase` 的 `expressions` / `order_by` 参数。
|
|
291
|
+
|
|
292
|
+
#### where 组合条件(FIQL 风格)
|
|
293
|
+
|
|
294
|
+
```
|
|
295
|
+
where = and_group ("," and_group)* , 表示 OR
|
|
296
|
+
and_group = unit (";" unit)* ; 表示 AND
|
|
297
|
+
unit = "(" where ")" | condition
|
|
298
|
+
condition = field OP value
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
**支持的操作符:**
|
|
302
|
+
|
|
303
|
+
| 语法 | 含义 | 示例 |
|
|
304
|
+
| ---------------- | -------------------- | ------------------------- |
|
|
305
|
+
| `field==value` | 等于 | `status==1` |
|
|
306
|
+
| `field!=value` | 不等 | `status!=0` |
|
|
307
|
+
| `field~=value` | 模糊包含 (ILIKE) | `name~=张` |
|
|
308
|
+
| `field>value` | 大于 | `age>18` |
|
|
309
|
+
| `field>=value` | 大于等于 | `age>=18` |
|
|
310
|
+
| `field<value` | 小于 | `age<60` |
|
|
311
|
+
| `field<=value` | 小于等于 | `age<=60` |
|
|
312
|
+
| `field~start~end`| 区间 (BETWEEN) | `age~18~60` |
|
|
313
|
+
|
|
314
|
+
```python
|
|
315
|
+
from fastapi_augment.db.sqlalchemy import parse_where
|
|
316
|
+
|
|
317
|
+
# 昵称含张 且 状态非禁用
|
|
318
|
+
expr = parse_where('nickname~=张;status!=0', User)
|
|
319
|
+
|
|
320
|
+
# 括号内 OR,与区间 AND
|
|
321
|
+
expr = parse_where('(nickname~=张,username~=王);age~20~30', User)
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
#### lookup 精确匹配
|
|
325
|
+
|
|
326
|
+
```python
|
|
327
|
+
from fastapi_augment.db.sqlalchemy import parse_lookup
|
|
328
|
+
|
|
329
|
+
# 单字段精确匹配
|
|
330
|
+
expr = parse_lookup('phone==13800138000', User, fields={'phone', 'email'})
|
|
331
|
+
|
|
332
|
+
# 多字段 AND(; 分隔)
|
|
333
|
+
expr = parse_lookup('phone==138;status==1', User, fields={'phone', 'status'})
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
#### 关键字搜索
|
|
337
|
+
|
|
338
|
+
```python
|
|
339
|
+
from fastapi_augment.db.sqlalchemy import parse_keyword
|
|
340
|
+
|
|
341
|
+
# 多字段 OR 模糊搜索
|
|
342
|
+
expr = parse_keyword('admin', 'username,email,nickname', User)
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
#### 排序
|
|
346
|
+
|
|
347
|
+
```python
|
|
348
|
+
from fastapi_augment.db.sqlalchemy import parse_sort
|
|
349
|
+
|
|
350
|
+
# - 前缀表示降序,无前缀为升序
|
|
351
|
+
order_by = parse_sort('-created_at,nickname', User)
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
#### 一键组合 — `build_query_expressions`
|
|
355
|
+
|
|
356
|
+
```python
|
|
357
|
+
from fastapi_augment.db.sqlalchemy import build_query_expressions
|
|
358
|
+
|
|
359
|
+
expressions, order_by = build_query_expressions(
|
|
360
|
+
User,
|
|
361
|
+
where='status!=0;age>=18',
|
|
362
|
+
q='admin',
|
|
363
|
+
q_field='username,nickname',
|
|
364
|
+
sort='-created_at',
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
# 直接传给 paginate / list
|
|
368
|
+
result = await user_repo.paginate(
|
|
369
|
+
session, page=1, size=10,
|
|
370
|
+
expressions=expressions, order_by=order_by,
|
|
371
|
+
)
|
|
372
|
+
```
|
|
373
|
+
|
|
285
374
|
### 数据库迁移 CLI — `fastapi-augment-migrate`
|
|
286
375
|
|
|
287
376
|
内置 Alembic 迁移工具,提供 `init` / `generate` / `upgrade` 三个子命令,开箱即用。
|
|
@@ -471,12 +560,12 @@ raise BadRequestError(detail='用户名不能为空')
|
|
|
471
560
|
raise TooManyRequestsError(retry_after=60)
|
|
472
561
|
```
|
|
473
562
|
|
|
474
|
-
### 日志管理 — `
|
|
563
|
+
### 日志管理 — `logger`
|
|
475
564
|
|
|
476
565
|
导入即生效:自动注入 `request_id` 到每条日志、接管 uvicorn/fastapi 日志输出。
|
|
477
566
|
|
|
478
567
|
```python
|
|
479
|
-
from fastapi_augment.
|
|
568
|
+
from fastapi_augment.logger import setup_logger, set_log_level
|
|
480
569
|
|
|
481
570
|
# 一键配置:控制台 + 按天轮转文件日志
|
|
482
571
|
setup_logger(log_dir='./logs', rotation='day', backup_count=30)
|
|
@@ -565,26 +654,30 @@ app.include_router(create_health_router(
|
|
|
565
654
|
|
|
566
655
|
### 配置管理 — `config`
|
|
567
656
|
|
|
568
|
-
基于 `pydantic-settings
|
|
657
|
+
基于 `pydantic-settings`,支持多种配置来源(环境变量、.env、JSON、YAML、TOML),通过不同类方法加载:
|
|
569
658
|
|
|
570
659
|
```python
|
|
571
|
-
from fastapi_augment.config import
|
|
660
|
+
from fastapi_augment.config import AugmentBaseSettings
|
|
572
661
|
|
|
573
|
-
class Settings(
|
|
662
|
+
class Settings(AugmentBaseSettings):
|
|
574
663
|
database_url: str
|
|
575
664
|
redis_url: str = ''
|
|
576
665
|
debug: bool = False
|
|
577
666
|
secret_key: str = 'change-me'
|
|
578
667
|
|
|
579
|
-
#
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
668
|
+
# 从环境变量加载
|
|
669
|
+
cfg = Settings.from_env(env_prefix='APP_', env_nested_delimiter='__')
|
|
670
|
+
|
|
671
|
+
# 从 .env 文件加载
|
|
672
|
+
cfg = Settings.from_dotenv('.env', env_prefix='APP_')
|
|
673
|
+
|
|
674
|
+
# 从 JSON / YAML / TOML 文件加载
|
|
675
|
+
cfg = Settings.from_json('config.json')
|
|
676
|
+
cfg = Settings.from_yaml('config.yaml')
|
|
677
|
+
cfg = Settings.from_toml('config.toml')
|
|
585
678
|
```
|
|
586
679
|
|
|
587
|
-
支持 `SettingsConfigDict` 的所有参数(`
|
|
680
|
+
支持 `SettingsConfigDict` 的所有参数(`env_prefix`、`secrets_dir`、`yaml_file` 等),
|
|
588
681
|
与模型字段值自动区分,无需关心分类。
|
|
589
682
|
|
|
590
683
|
### 中间件 — `middlewares`
|
|
@@ -610,13 +703,15 @@ fastapi_augment/
|
|
|
610
703
|
│ └── utils/
|
|
611
704
|
│ └── strings.py # 字符串工具 / JSON 序列化
|
|
612
705
|
├── config/
|
|
613
|
-
│
|
|
706
|
+
│ ├── base_settings.py # AugmentBaseSettings 配置管理
|
|
707
|
+
│ └── database_settings.py # DatabaseSettings 数据库配置
|
|
614
708
|
├── db/
|
|
615
709
|
│ └── sqlalchemy/
|
|
616
710
|
│ ├── engine.py # EngineManager / NodeConfig / ClusterTopology
|
|
617
711
|
│ ├── session.py # SessionFactory(读写分离)
|
|
618
712
|
│ ├── model_base.py # ModelBase(ULID 主键)
|
|
619
713
|
│ ├── repository_base.py # RepositoryBase(泛型仓储 + paginate)
|
|
714
|
+
│ ├── query_parser.py # 查询解析器(where / lookup / keyword / sort)
|
|
620
715
|
│ ├── migrate.py # 数据库迁移 CLI
|
|
621
716
|
│ ├── migrations/ # Alembic 迁移环境(env.py / script.py.mako)
|
|
622
717
|
│ └── mixins/ # Timestamp / Audit / SoftDelete
|
|
@@ -624,11 +719,11 @@ fastapi_augment/
|
|
|
624
719
|
│ ├── checker.py # BaseChecker / CheckResult / HealthResponse
|
|
625
720
|
│ ├── checkers.py # AppChecker / DatabaseChecker
|
|
626
721
|
│ └── router.py # create_health_router()
|
|
627
|
-
├──
|
|
628
|
-
│ ├──
|
|
722
|
+
├── logger/
|
|
723
|
+
│ ├── record_factory.py # request_id 注入工厂
|
|
629
724
|
│ ├── filters.py # UvicornNameRewriteFilter
|
|
630
725
|
│ ├── handlers.py # 多进程安全轮转处理器
|
|
631
|
-
│ └──
|
|
726
|
+
│ └── setup.py # setup_logger / set_log_level / set_log_format
|
|
632
727
|
├── middlewares/
|
|
633
728
|
│ ├── base.py # BaseASGIMiddleware
|
|
634
729
|
│ └── request_id.py # RequestId 中间件
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.1.4
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""
|
|
2
2
|
@Author : hangu
|
|
3
3
|
@CreateDate : 2026/9/4
|
|
4
|
-
@Description :
|
|
4
|
+
@Description : 全局常量与统一默认错误文案
|
|
5
5
|
"""
|
|
6
6
|
from starlette import status
|
|
7
7
|
|
|
@@ -21,6 +21,7 @@ DEFAULT_ERR_MSG: dict[int, str] = {
|
|
|
21
21
|
status.HTTP_413_CONTENT_TOO_LARGE: 'Payload too large',
|
|
22
22
|
status.HTTP_414_URI_TOO_LONG: 'URI too long',
|
|
23
23
|
status.HTTP_415_UNSUPPORTED_MEDIA_TYPE: 'Unsupported media type',
|
|
24
|
+
status.HTTP_422_UNPROCESSABLE_CONTENT: 'Unprocessable entity',
|
|
24
25
|
status.HTTP_423_LOCKED: 'Locked',
|
|
25
26
|
status.HTTP_429_TOO_MANY_REQUESTS: 'Too many requests',
|
|
26
27
|
}
|
{fastapi_augment-0.1.2 → fastapi_augment-0.1.4}/src/fastapi_augment/common/exception_handlers.py
RENAMED
|
@@ -27,11 +27,11 @@ async def base_http_error_handler(
|
|
|
27
27
|
_request: Request,
|
|
28
28
|
exc: BaseHttpError,
|
|
29
29
|
) -> JSONResponse:
|
|
30
|
-
"""处理 BaseHttpError
|
|
30
|
+
"""处理 BaseHttpError 及其所有子类(统一业务异常)
|
|
31
31
|
|
|
32
32
|
将业务异常转换为统一 APIResponse 格式,
|
|
33
33
|
HTTP 状态码与 exc.status_code 一致,
|
|
34
|
-
body.code 同样使用 HTTP 状态码,body.message 使用 exc.detail
|
|
34
|
+
body.code 同样使用 HTTP 状态码,body.message 使用 exc.detail
|
|
35
35
|
|
|
36
36
|
Args:
|
|
37
37
|
_request: Starlette Request 对象
|
|
@@ -54,11 +54,11 @@ async def http_exception_handler(
|
|
|
54
54
|
_request: Request,
|
|
55
55
|
exc: HTTPException,
|
|
56
56
|
) -> JSONResponse:
|
|
57
|
-
"""处理 Starlette/FastAPI 原生 HTTPException
|
|
57
|
+
"""处理 Starlette/FastAPI 原生 HTTPException
|
|
58
58
|
|
|
59
|
-
覆盖 FastAPI 默认处理器,将响应格式统一为 APIResponse
|
|
59
|
+
覆盖 FastAPI 默认处理器,将响应格式统一为 APIResponse
|
|
60
60
|
注意:BaseHttpError 继承自 HTTPException,但 FastAPI 会优先匹配
|
|
61
|
-
更具体的处理器(base_http_error_handler
|
|
61
|
+
更具体的处理器(base_http_error_handler),所以此处不会拦截业务异常
|
|
62
62
|
|
|
63
63
|
Args:
|
|
64
64
|
_request: Starlette Request 对象
|
|
@@ -84,9 +84,9 @@ async def validation_exception_handler(
|
|
|
84
84
|
_request: Request,
|
|
85
85
|
exc: RequestValidationError,
|
|
86
86
|
) -> JSONResponse:
|
|
87
|
-
"""处理 Pydantic 请求参数校验异常(422
|
|
87
|
+
"""处理 Pydantic 请求参数校验异常(422)
|
|
88
88
|
|
|
89
|
-
将校验错误详情提取到 extra.errors
|
|
89
|
+
将校验错误详情提取到 extra.errors 中,方便前端定位具体字段
|
|
90
90
|
|
|
91
91
|
Args:
|
|
92
92
|
_request: Starlette Request 对象
|
|
@@ -121,10 +121,10 @@ async def general_exception_handler(
|
|
|
121
121
|
_request: Request,
|
|
122
122
|
exc: Exception,
|
|
123
123
|
) -> JSONResponse:
|
|
124
|
-
"""
|
|
124
|
+
"""处理所有未被捕获的异常(兜底)
|
|
125
125
|
|
|
126
126
|
记录完整异常日志(含堆栈),但响应体只返回通用提示,
|
|
127
|
-
避免将内部实现细节(堆栈、SQL
|
|
127
|
+
避免将内部实现细节(堆栈、SQL 等)暴露给客户端
|
|
128
128
|
|
|
129
129
|
Args:
|
|
130
130
|
_request: Starlette Request 对象
|
|
@@ -144,7 +144,7 @@ async def general_exception_handler(
|
|
|
144
144
|
# ===================== 一键注册 =====================
|
|
145
145
|
|
|
146
146
|
def register_exception_handlers(app: FastAPI) -> None:
|
|
147
|
-
"""将全部统一异常处理器注册到 FastAPI
|
|
147
|
+
"""将全部统一异常处理器注册到 FastAPI 应用
|
|
148
148
|
|
|
149
149
|
注册后,以下异常会被转换为统一的 APIResponse 格式:
|
|
150
150
|
- BaseHttpError 及子类 → 对应 HTTP 状态码
|
|
@@ -13,7 +13,7 @@ from .constants import DEFAULT_ERR_MSG
|
|
|
13
13
|
|
|
14
14
|
# ===================== 通用基类:统一封装 detail + headers 逻辑 =====================
|
|
15
15
|
class BaseHttpError(HTTPException):
|
|
16
|
-
"""统一HTTP异常基类,所有4xx异常继承此类,原生兼容starlette.HTTPException
|
|
16
|
+
"""统一HTTP异常基类,所有4xx异常继承此类,原生兼容starlette.HTTPException
|
|
17
17
|
|
|
18
18
|
子类只需声明 ``_status_code`` 类变量,无需重写 __init__::
|
|
19
19
|
|
|
@@ -38,7 +38,7 @@ class BaseHttpError(HTTPException):
|
|
|
38
38
|
detail: str | None = None,
|
|
39
39
|
headers: dict[str, Any] | None = None,
|
|
40
40
|
):
|
|
41
|
-
"""初始化http
|
|
41
|
+
"""初始化http业务异常
|
|
42
42
|
|
|
43
43
|
Args:
|
|
44
44
|
status_code: http响应状态码,不传则读取子类的 _status_code 类变量
|
|
@@ -62,82 +62,82 @@ class BaseHttpError(HTTPException):
|
|
|
62
62
|
# ===================== 各类4xx异常子类(极简声明,无重复__init__) =====================
|
|
63
63
|
# 子类只需声明 _status_code 类变量,__init__ 由基类统一处理
|
|
64
64
|
class BadRequestError(BaseHttpError):
|
|
65
|
-
"""400
|
|
65
|
+
"""400 请求错误"""
|
|
66
66
|
_status_code = status.HTTP_400_BAD_REQUEST
|
|
67
67
|
|
|
68
68
|
|
|
69
69
|
class UnauthorizedError(BaseHttpError):
|
|
70
|
-
"""401
|
|
70
|
+
"""401 未授权错误"""
|
|
71
71
|
_status_code = status.HTTP_401_UNAUTHORIZED
|
|
72
72
|
|
|
73
73
|
|
|
74
74
|
class PaymentRequiredError(BaseHttpError):
|
|
75
|
-
"""402
|
|
75
|
+
"""402 需要付费错误"""
|
|
76
76
|
_status_code = status.HTTP_402_PAYMENT_REQUIRED
|
|
77
77
|
|
|
78
78
|
|
|
79
79
|
class ForbiddenError(BaseHttpError):
|
|
80
|
-
"""403
|
|
80
|
+
"""403 禁止访问错误"""
|
|
81
81
|
_status_code = status.HTTP_403_FORBIDDEN
|
|
82
82
|
|
|
83
83
|
|
|
84
84
|
class NotFoundError(BaseHttpError):
|
|
85
|
-
"""404
|
|
85
|
+
"""404 未找到资源"""
|
|
86
86
|
_status_code = status.HTTP_404_NOT_FOUND
|
|
87
87
|
|
|
88
88
|
|
|
89
89
|
class MethodNotAllowedError(BaseHttpError):
|
|
90
|
-
"""405
|
|
90
|
+
"""405 请求方法不允许"""
|
|
91
91
|
_status_code = status.HTTP_405_METHOD_NOT_ALLOWED
|
|
92
92
|
|
|
93
93
|
|
|
94
94
|
class NotAcceptableError(BaseHttpError):
|
|
95
|
-
"""406
|
|
95
|
+
"""406 客户端不支持返回格式"""
|
|
96
96
|
_status_code = status.HTTP_406_NOT_ACCEPTABLE
|
|
97
97
|
|
|
98
98
|
|
|
99
99
|
class RequestTimeoutError(BaseHttpError):
|
|
100
|
-
"""408
|
|
100
|
+
"""408 请求超时"""
|
|
101
101
|
_status_code = status.HTTP_408_REQUEST_TIMEOUT
|
|
102
102
|
|
|
103
103
|
|
|
104
104
|
class ConflictError(BaseHttpError):
|
|
105
|
-
"""409
|
|
105
|
+
"""409 资源冲突"""
|
|
106
106
|
_status_code = status.HTTP_409_CONFLICT
|
|
107
107
|
|
|
108
108
|
|
|
109
109
|
class GoneError(BaseHttpError):
|
|
110
|
-
"""410
|
|
110
|
+
"""410 资源已永久删除"""
|
|
111
111
|
_status_code = status.HTTP_410_GONE
|
|
112
112
|
|
|
113
113
|
|
|
114
114
|
class PreconditionFailedError(BaseHttpError):
|
|
115
|
-
"""412
|
|
115
|
+
"""412 前置校验失败"""
|
|
116
116
|
_status_code = status.HTTP_412_PRECONDITION_FAILED
|
|
117
117
|
|
|
118
118
|
|
|
119
119
|
class PayloadTooLargeError(BaseHttpError):
|
|
120
|
-
"""413
|
|
120
|
+
"""413 请求体过大"""
|
|
121
121
|
_status_code = status.HTTP_413_CONTENT_TOO_LARGE
|
|
122
122
|
|
|
123
123
|
|
|
124
124
|
class URITooLongError(BaseHttpError):
|
|
125
|
-
"""414 URI
|
|
125
|
+
"""414 URI链接过长"""
|
|
126
126
|
_status_code = status.HTTP_414_URI_TOO_LONG
|
|
127
127
|
|
|
128
128
|
|
|
129
129
|
class UnsupportedMediaTypeError(BaseHttpError):
|
|
130
|
-
"""415
|
|
130
|
+
"""415 不支持的请求媒体类型"""
|
|
131
131
|
_status_code = status.HTTP_415_UNSUPPORTED_MEDIA_TYPE
|
|
132
132
|
|
|
133
133
|
|
|
134
134
|
class LockedError(BaseHttpError):
|
|
135
|
-
"""423
|
|
135
|
+
"""423 资源锁定"""
|
|
136
136
|
_status_code = status.HTTP_423_LOCKED
|
|
137
137
|
|
|
138
138
|
|
|
139
139
|
class TooManyRequestsError(BaseHttpError):
|
|
140
|
-
"""429 请求过于频繁(限流专用,支持retry_after
|
|
140
|
+
"""429 请求过于频繁(限流专用,支持retry_after快捷参数)"""
|
|
141
141
|
_status_code = status.HTTP_429_TOO_MANY_REQUESTS
|
|
142
142
|
|
|
143
143
|
def __init__(
|