toms-fast 0.2.1__py3-none-any.whl

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.
Files changed (60) hide show
  1. toms_fast-0.2.1.dist-info/METADATA +467 -0
  2. toms_fast-0.2.1.dist-info/RECORD +60 -0
  3. toms_fast-0.2.1.dist-info/WHEEL +4 -0
  4. toms_fast-0.2.1.dist-info/entry_points.txt +2 -0
  5. tomskit/__init__.py +0 -0
  6. tomskit/celery/README.md +693 -0
  7. tomskit/celery/__init__.py +4 -0
  8. tomskit/celery/celery.py +306 -0
  9. tomskit/celery/config.py +377 -0
  10. tomskit/cli/__init__.py +207 -0
  11. tomskit/cli/__main__.py +8 -0
  12. tomskit/cli/scaffold.py +123 -0
  13. tomskit/cli/templates/__init__.py +42 -0
  14. tomskit/cli/templates/base.py +348 -0
  15. tomskit/cli/templates/celery.py +101 -0
  16. tomskit/cli/templates/extensions.py +213 -0
  17. tomskit/cli/templates/fastapi.py +400 -0
  18. tomskit/cli/templates/migrations.py +281 -0
  19. tomskit/cli/templates_config.py +122 -0
  20. tomskit/logger/README.md +466 -0
  21. tomskit/logger/__init__.py +4 -0
  22. tomskit/logger/config.py +106 -0
  23. tomskit/logger/logger.py +290 -0
  24. tomskit/py.typed +0 -0
  25. tomskit/redis/README.md +462 -0
  26. tomskit/redis/__init__.py +6 -0
  27. tomskit/redis/config.py +85 -0
  28. tomskit/redis/redis_pool.py +87 -0
  29. tomskit/redis/redis_sync.py +66 -0
  30. tomskit/server/__init__.py +47 -0
  31. tomskit/server/config.py +117 -0
  32. tomskit/server/exceptions.py +412 -0
  33. tomskit/server/middleware.py +371 -0
  34. tomskit/server/parser.py +312 -0
  35. tomskit/server/resource.py +464 -0
  36. tomskit/server/server.py +276 -0
  37. tomskit/server/type.py +263 -0
  38. tomskit/sqlalchemy/README.md +590 -0
  39. tomskit/sqlalchemy/__init__.py +20 -0
  40. tomskit/sqlalchemy/config.py +125 -0
  41. tomskit/sqlalchemy/database.py +125 -0
  42. tomskit/sqlalchemy/pagination.py +359 -0
  43. tomskit/sqlalchemy/property.py +19 -0
  44. tomskit/sqlalchemy/sqlalchemy.py +131 -0
  45. tomskit/sqlalchemy/types.py +32 -0
  46. tomskit/task/README.md +67 -0
  47. tomskit/task/__init__.py +4 -0
  48. tomskit/task/task_manager.py +124 -0
  49. tomskit/tools/README.md +63 -0
  50. tomskit/tools/__init__.py +18 -0
  51. tomskit/tools/config.py +70 -0
  52. tomskit/tools/warnings.py +37 -0
  53. tomskit/tools/woker.py +81 -0
  54. tomskit/utils/README.md +666 -0
  55. tomskit/utils/README_SERIALIZER.md +644 -0
  56. tomskit/utils/__init__.py +35 -0
  57. tomskit/utils/fields.py +434 -0
  58. tomskit/utils/marshal_utils.py +137 -0
  59. tomskit/utils/response_utils.py +13 -0
  60. tomskit/utils/serializers.py +447 -0
@@ -0,0 +1,400 @@
1
+ """
2
+ FastAPI 模板模块
3
+ 包含所有 FastAPI 相关的文件模板
4
+ """
5
+
6
+ from typing import Callable
7
+
8
+
9
+ def get_fastapi_templates(project_name: str) -> dict[str, Callable[[], str]]:
10
+ """获取 FastAPI 模板"""
11
+ return {
12
+ "main_py": lambda: f'''"""
13
+ {project_name} 应用入口
14
+ """
15
+
16
+ import os
17
+ from pathlib import Path
18
+ from dotenv import load_dotenv
19
+
20
+ from tomskit.server import FastApp
21
+ from app.config import setup_config
22
+ from app.middleware import setup_middleware
23
+ from app.controllers.users.module import init_user_module
24
+ from extensions import init_all_extensions
25
+
26
+
27
+ # 加载环境变量
28
+ env_path = Path(__file__).parent / ".env"
29
+ if env_path.exists():
30
+ load_dotenv(env_path)
31
+ else:
32
+ print("⚠️ 警告: .env 文件不存在,请从 .env.example 复制并配置")
33
+
34
+
35
+ # 创建应用实例
36
+ app = FastApp(
37
+ title="{project_name}",
38
+ description="基于 toms-fast 的 FastAPI 应用",
39
+ version="0.1.0"
40
+ )
41
+
42
+ # 设置应用根路径
43
+ app.set_app_root_path(__file__)
44
+
45
+ # 初始化配置
46
+ setup_config(app)
47
+
48
+ # 配置中间件
49
+ setup_middleware(app)
50
+
51
+ # 统一初始化所有扩展(logger, database, redis 等)
52
+ init_all_extensions(app)
53
+
54
+ # 注册控制器
55
+ init_user_module(app)
56
+
57
+ # 健康检查端点
58
+ @app.get("/health")
59
+ async def health_check():
60
+ """健康检查"""
61
+ return {{"status": "ok", "service": "{project_name}"}}
62
+
63
+
64
+ if __name__ == "__main__":
65
+ import uvicorn
66
+ uvicorn.run(
67
+ "main:app",
68
+ host="0.0.0.0",
69
+ port=8000,
70
+ reload=True
71
+ )
72
+ ''',
73
+
74
+ "app_init_py": lambda: "",
75
+
76
+ "config_py": lambda: '''"""
77
+ 应用配置管理
78
+ """
79
+
80
+ from tomskit.server import FastApp
81
+
82
+
83
+ def setup_config(app: FastApp):
84
+ """设置应用配置"""
85
+ # 从环境变量加载配置
86
+ # 配置会自动从环境变量读取(通过 pydantic-settings)
87
+ pass
88
+ ''',
89
+
90
+ "middleware_init_py": lambda: '''"""
91
+ 中间件统一注册
92
+ """
93
+
94
+ from tomskit.server import FastApp
95
+
96
+ from . import request_id, resource_cleanup
97
+ # 在这里导入更多中间件模块
98
+ # from . import cors, auth, rate_limit
99
+
100
+
101
+ def setup_middleware(app: FastApp):
102
+ """
103
+ 统一注册所有中间件
104
+
105
+ 中间件执行顺序(从外到内,按注册顺序):
106
+ 1. request_id - 请求 ID 追踪(最外层)
107
+ 2. resource_cleanup - 资源清理(最内层)
108
+
109
+ 注意:中间件的注册顺序很重要,后注册的中间件会更靠近应用核心
110
+ """
111
+ # 按顺序注册中间件
112
+ request_id.setup(app)
113
+ resource_cleanup.setup(app)
114
+
115
+ # 注册更多中间件
116
+ # cors.setup(app)
117
+ # auth.setup(app)
118
+ # rate_limit.setup(app)
119
+ ''',
120
+
121
+ "middleware_request_id_py": lambda: '''"""
122
+ 请求 ID 追踪中间件
123
+ """
124
+
125
+ from tomskit.server import FastApp, RequestIDMiddleware
126
+
127
+
128
+ def setup(app: FastApp):
129
+ """
130
+ 配置请求 ID 追踪中间件
131
+
132
+ 功能:
133
+ - 自动处理 X-Request-ID 请求头
134
+ - 如果请求中没有 X-Request-ID,自动生成 UUID
135
+ - 将请求 ID 设置到日志上下文中,用于分布式追踪
136
+ - 在响应头中添加 X-Request-ID
137
+ """
138
+ app.add_middleware(RequestIDMiddleware)
139
+ ''',
140
+
141
+ "middleware_resource_cleanup_py": lambda: '''"""
142
+ 资源清理中间件
143
+ """
144
+
145
+ from tomskit.server import FastApp, ResourceCleanupMiddleware
146
+ from tomskit.sqlalchemy.database import DatabaseCleanupStrategy
147
+ from tomskit.redis.redis_pool import RedisCleanupStrategy
148
+
149
+
150
+ def setup(app: FastApp):
151
+ """
152
+ 配置资源清理中间件
153
+
154
+ 功能:
155
+ - 自动清理数据库会话,防止资源泄漏
156
+ - 自动清理 Redis 连接,防止资源泄漏
157
+ - 在请求完成后自动执行清理,即使发生异常也会清理
158
+ """
159
+ app.add_middleware(
160
+ ResourceCleanupMiddleware,
161
+ strategies=[
162
+ DatabaseCleanupStrategy(),
163
+ RedisCleanupStrategy(),
164
+ ]
165
+ )
166
+ ''',
167
+
168
+ "controllers_init_py": lambda: '''"""
169
+ Controllers 目录
170
+ """
171
+
172
+ ''',
173
+
174
+ "users_init_py": lambda: '''"""
175
+ 用户控制器模块
176
+ """
177
+
178
+ from .module import init_user_module
179
+
180
+ __all__ = ["init_user_module"]
181
+ ''',
182
+
183
+ "users_resources_py": lambda: '''"""
184
+ 用户资源 API
185
+ """
186
+
187
+ from fastapi import Request, HTTPException
188
+
189
+ from tomskit.server import Resource, api_doc, register_resource
190
+ from .schemas import UserResponse, UserCreate, UserUpdate
191
+
192
+
193
+ @register_resource(module="users", path="/users", tags=["用户管理"])
194
+ class UserResource(Resource):
195
+ """用户资源"""
196
+
197
+ @api_doc(
198
+ summary="获取用户列表",
199
+ description="获取所有用户列表,支持分页",
200
+ response_model=list[UserResponse],
201
+ responses={{
202
+ 200: "成功",
203
+ 500: "服务器错误"
204
+ }}
205
+ )
206
+ async def get(self, request: Request):
207
+ """获取用户列表"""
208
+ # TODO: 从数据库获取用户列表
209
+ return [
210
+ {{"id": 1, "name": "Alice", "email": "alice@example.com"}},
211
+ {{"id": 2, "name": "Bob", "email": "bob@example.com"}}
212
+ ]
213
+
214
+ @api_doc(
215
+ summary="创建用户",
216
+ description="创建新用户",
217
+ response_model=UserResponse,
218
+ status_code=201,
219
+ responses={{
220
+ 201: "用户创建成功",
221
+ 400: "请求参数错误",
222
+ 409: "用户已存在"
223
+ }}
224
+ )
225
+ async def post(self, request: Request):
226
+ """创建用户"""
227
+ data = await request.json()
228
+ # TODO: 验证数据并创建用户
229
+ return {{
230
+ "id": 3,
231
+ "name": data.get("name", ""),
232
+ "email": data.get("email", "")
233
+ }}
234
+
235
+ @api_doc(
236
+ summary="获取用户详情",
237
+ description="根据用户 ID 获取用户详情",
238
+ response_model=UserResponse,
239
+ path="/users/{{user_id}}",
240
+ responses={{
241
+ 200: "成功",
242
+ 404: "用户不存在"
243
+ }}
244
+ )
245
+ async def get(self, request: Request):
246
+ """获取用户详情"""
247
+ user_id = request.path_params.get("user_id")
248
+ if not user_id:
249
+ raise HTTPException(status_code=404, detail="用户不存在")
250
+ # TODO: 从数据库获取用户
251
+ return {{"id": int(user_id), "name": "Alice", "email": "alice@example.com"}}
252
+
253
+ @api_doc(
254
+ summary="更新用户",
255
+ description="更新用户信息",
256
+ response_model=UserResponse,
257
+ path="/users/{{user_id}}",
258
+ responses={{
259
+ 200: "更新成功",
260
+ 404: "用户不存在",
261
+ 400: "请求参数错误"
262
+ }}
263
+ )
264
+ async def put(self, request: Request):
265
+ """更新用户"""
266
+ user_id = request.path_params.get("user_id")
267
+ data = await request.json()
268
+ # TODO: 更新用户信息
269
+ return {{"id": int(user_id), "name": data.get("name", ""), "email": data.get("email", "")}}
270
+
271
+ @api_doc(
272
+ summary="删除用户",
273
+ description="删除指定用户",
274
+ path="/users/{{user_id}}",
275
+ responses={{
276
+ 204: "删除成功",
277
+ 404: "用户不存在"
278
+ }}
279
+ )
280
+ async def delete(self, request: Request):
281
+ """删除用户"""
282
+ user_id = request.path_params.get("user_id")
283
+ # TODO: 删除用户
284
+ return None
285
+ ''',
286
+
287
+ "users_schemas_py": lambda: '''"""
288
+ 用户数据模型
289
+ """
290
+
291
+ from pydantic import BaseModel, EmailStr
292
+
293
+
294
+ class UserBase(BaseModel):
295
+ """用户基础模型"""
296
+ name: str
297
+ email: EmailStr
298
+
299
+
300
+ class UserCreate(UserBase):
301
+ """创建用户请求模型"""
302
+ pass
303
+
304
+
305
+ class UserUpdate(BaseModel):
306
+ """更新用户请求模型"""
307
+ name: str | None = None
308
+ email: EmailStr | None = None
309
+
310
+
311
+ class UserResponse(UserBase):
312
+ """用户响应模型"""
313
+ id: int
314
+
315
+ class Config:
316
+ from_attributes = True
317
+ ''',
318
+
319
+ "users_module_py": lambda: '''"""
320
+ 用户控制器初始化
321
+ """
322
+
323
+ from tomskit.server import FastApp, FastModule
324
+
325
+
326
+ def init_user_module(app: FastApp):
327
+ """初始化用户控制器"""
328
+ # 创建用户控制器模块
329
+ user_module = FastModule(name="users")
330
+
331
+ # 创建路由(前缀会自动添加到所有资源路径)
332
+ user_module.create_router(prefix="/api/v1")
333
+
334
+ # 自动注册所有标记为 "users" 模块的资源
335
+ user_module.auto_register_resources()
336
+
337
+ # 配置 CORS(如果需要)
338
+ # user_module.setup_cors(
339
+ # allow_origins=["http://localhost:3000"],
340
+ # allow_credentials=True
341
+ # )
342
+
343
+ # 挂载控制器到主应用
344
+ app.mount("/", user_module)
345
+
346
+ print("✅ 用户控制器初始化成功")
347
+ ''',
348
+
349
+ "models_init_py": lambda: '''"""
350
+ 数据库模型模块
351
+ 导出所有模型和 Base
352
+ """
353
+
354
+ from tomskit.sqlalchemy import SQLAlchemy
355
+
356
+ # 导入所有模型,确保它们被注册到 Base.metadata
357
+ from .user import User # noqa: F401
358
+
359
+ # 导出 Base 供 Alembic 使用
360
+ Base = SQLAlchemy.Model
361
+ ''',
362
+
363
+ "user_model_py": lambda: '''"""
364
+ 用户数据库模型
365
+ """
366
+
367
+ from sqlalchemy import Column, Integer, String
368
+ from tomskit.sqlalchemy import SQLAlchemy
369
+
370
+
371
+ class User(SQLAlchemy.Model):
372
+ """用户模型"""
373
+ __tablename__ = "users"
374
+
375
+ id = Column(Integer, primary_key=True, index=True)
376
+ name = Column(String(100), nullable=False)
377
+ email = Column(String(255), unique=True, nullable=False, index=True)
378
+
379
+ def __repr__(self):
380
+ return f"<User(id={self.id}, name={self.name}, email={self.email})>"
381
+ ''',
382
+
383
+ "test_users_py": lambda: '''"""
384
+ 用户模块测试
385
+ """
386
+
387
+ import pytest
388
+ from httpx import AsyncClient
389
+ from main import app
390
+
391
+
392
+ @pytest.mark.asyncio
393
+ async def test_get_users():
394
+ """测试获取用户列表"""
395
+ async with AsyncClient(app=app, base_url="http://test") as client:
396
+ response = await client.get("/api/v1/users")
397
+ assert response.status_code == 200
398
+ assert isinstance(response.json(), list)
399
+ ''',
400
+ }
@@ -0,0 +1,281 @@
1
+ """
2
+ 数据库迁移模板模块
3
+ 包含 Alembic 相关的文件模板
4
+ """
5
+
6
+ from typing import Callable
7
+
8
+
9
+ def get_migrations_templates(project_name: str) -> dict[str, Callable[[], str]]:
10
+ """获取数据库迁移模板"""
11
+ return {
12
+ "alembic_ini": lambda: f'''# A generic, {project_name} database configuration.
13
+
14
+ [alembic]
15
+ # path to migration scripts
16
+ script_location = .
17
+
18
+ # template used to generate migration file names; The default value is %%(rev)s_%%(slug)s
19
+ # Uncomment the line below if you want the files to be prepended with date and time
20
+ # file_template = %%%%Y%%%%m%%%%d_%%%%H%%%%M%%%%S_%%%(rev)s_%%%(slug)s
21
+
22
+ # sys.path path, will be prepended to sys.path if present.
23
+ # defaults to the current working directory.
24
+ prepend_sys_path = .
25
+
26
+ # timezone to use when rendering the date within the migration file
27
+ # as well as the filename.
28
+ # If specified, requires the python-dateutil library that can be
29
+ # installed by adding `alembic[tz]` to the pip requirements
30
+ # string value is passed to dateutil.tz.gettz()
31
+ # leave blank for localtime
32
+ # timezone =
33
+
34
+ # max length of characters to apply to the
35
+ # "slug" field
36
+ # truncate_slug_length = 40
37
+
38
+ # set to 'true' to run the environment during
39
+ # the 'revision' command, regardless of autogenerate
40
+ # revision_environment = false
41
+
42
+ # set to 'true' to allow .pyc and .pyo files without
43
+ # a source .py file to be detected as revisions in the
44
+ # versions/ directory
45
+ # sourceless = false
46
+
47
+ # version location specification; This defaults
48
+ # to migrations/versions. When using multiple version
49
+ # directories, initial revisions must be specified with --version-path.
50
+ # The path separator used here should be the separator specified by "version_path_separator" below.
51
+ # version_locations = %%(here)s/bar:%%(here)s/bat:migrations/versions
52
+
53
+ # version path separator; As mentioned above, this is the character used to split
54
+ # version_locations. The default within new alembic.ini files is "os", which uses os.pathsep.
55
+ # If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas.
56
+ # Valid values for version_path_separator are:
57
+ #
58
+ # version_path_separator = :
59
+ # version_path_separator = ;
60
+ # version_path_separator = space
61
+ version_path_separator = os # Use os.pathsep. Default configuration used for new projects.
62
+
63
+ # set to 'true' to search source files recursively
64
+ # in each "version_locations" directory
65
+ # new in Alembic version 1.10
66
+ # recursive_version_locations = false
67
+
68
+ # the output encoding used when revision files
69
+ # are written from script.py.mako
70
+ # output_encoding = utf-8
71
+
72
+ sqlalchemy.url = driver://user:pass@localhost/dbname
73
+
74
+
75
+ [post_write_hooks]
76
+ # post_write_hooks defines scripts or Python functions that are run
77
+ # on newly generated revision scripts. See the documentation for further
78
+ # detail and examples
79
+
80
+ # format using "black" - use the console_scripts runner, against the "black" entrypoint
81
+ # hooks = black
82
+ # black.type = console_scripts
83
+ # black.entrypoint = black
84
+ # black.options = -l 79 REVISION_SCRIPT_FILENAME
85
+
86
+ # lint with attempts to fix using "ruff" - use the exec runner, execute a binary
87
+ # hooks = ruff
88
+ # ruff.type = exec
89
+ # ruff.executable = %%(here)s/.venv/bin/ruff
90
+ # ruff.options = --fix REVISION_SCRIPT_FILENAME
91
+
92
+ # Logging configuration
93
+ [loggers]
94
+ keys = root,sqlalchemy,alembic
95
+
96
+ [handlers]
97
+ keys = console
98
+
99
+ [formatters]
100
+ keys = generic
101
+
102
+ [logger_root]
103
+ level = WARN
104
+ handlers = console
105
+ qualname =
106
+
107
+ [logger_sqlalchemy]
108
+ level = WARN
109
+ handlers =
110
+ qualname = sqlalchemy.engine
111
+
112
+ [logger_alembic]
113
+ level = INFO
114
+ handlers =
115
+ qualname = alembic
116
+
117
+ [handler_console]
118
+ class = StreamHandler
119
+ args = (sys.stderr,)
120
+ level = NOTSET
121
+ formatter = generic
122
+
123
+ [formatter_generic]
124
+ format = %%(levelname)-5.5s [%%(name)s] %%(message)s
125
+ datefmt = %%%%H:%%%%M:%%%%S
126
+ ''',
127
+
128
+ "migrations_env_py": lambda: f'''"""
129
+ Alembic {project_name} 环境配置文件
130
+ 用于数据库迁移
131
+ """
132
+
133
+ import sys
134
+ from logging.config import fileConfig
135
+ from pathlib import Path
136
+
137
+ from sqlalchemy import pool
138
+ from sqlalchemy.engine import Connection
139
+ from sqlalchemy import create_engine
140
+
141
+ from alembic import context
142
+
143
+ # 添加项目根目录到 Python 路径
144
+ # migrations 在 backend/migrations,backend 在项目根目录下
145
+ project_root = Path(__file__).parent.parent # backend 目录
146
+ sys.path.insert(0, str(project_root))
147
+
148
+ # 导入数据库配置和模型
149
+ try:
150
+ from app.models import Base # 导入所有模型
151
+ target_metadata = Base.metadata
152
+ except ImportError:
153
+ # 如果模型还没有定义,创建一个空的 metadata
154
+ from sqlalchemy import MetaData
155
+ target_metadata = MetaData()
156
+ print("⚠️ 警告: 未找到 app.models,使用空的 metadata")
157
+
158
+ # this is the Alembic Config object, which provides
159
+ # access to the values within the .ini file in use.
160
+ config = context.config
161
+
162
+ # Interpret the config file for Python logging.
163
+ # This line sets up loggers basically.
164
+ if config.config_file_name is not None:
165
+ fileConfig(config.config_file_name)
166
+
167
+ # other values from the config, defined by the needs of env.py,
168
+ # can be acquired:
169
+ # my_important_option = config.get_main_option("my_important_option")
170
+ # ... etc.
171
+
172
+
173
+ def get_url():
174
+ """从环境变量获取数据库 URL"""
175
+ from dotenv import load_dotenv
176
+ from tomskit.sqlalchemy import DatabaseConfig
177
+
178
+ # 加载环境变量(.env 在 backend 目录下)
179
+ env_path = Path(__file__).parent.parent / ".env"
180
+ if env_path.exists():
181
+ load_dotenv(env_path)
182
+
183
+ # 获取数据库配置
184
+ db_config = DatabaseConfig()
185
+ # 使用同步 URI 用于 Alembic(因为 Alembic 需要同步连接)
186
+ return db_config.SQLALCHEMY_DATABASE_SYNC_URI
187
+
188
+
189
+ def run_migrations_offline() -> None:
190
+ """Run migrations in 'offline' mode.
191
+
192
+ This configures the context with just a URL
193
+ and not an Engine, though an Engine is acceptable
194
+ here as well. By skipping the Engine creation
195
+ we don't even need a DBAPI to be available.
196
+
197
+ Calls to context.execute() here emit the given string to the
198
+ script output.
199
+
200
+ """
201
+ url = get_url()
202
+ context.configure(
203
+ url=url,
204
+ target_metadata=target_metadata,
205
+ literal_binds=True,
206
+ dialect_opts={{"paramstyle": "named"}},
207
+ )
208
+
209
+ with context.begin_transaction():
210
+ context.run_migrations()
211
+
212
+
213
+ def do_run_migrations(connection: Connection) -> None:
214
+ context.configure(connection=connection, target_metadata=target_metadata)
215
+
216
+ with context.begin_transaction():
217
+ context.run_migrations()
218
+
219
+
220
+ def run_migrations_online() -> None:
221
+ """Run migrations in 'online' mode."""
222
+
223
+ configuration = config.get_section(config.config_ini_section)
224
+ configuration["sqlalchemy.url"] = get_url()
225
+
226
+ # 使用同步引擎(Alembic 需要同步连接)
227
+ connectable = create_engine(
228
+ configuration["sqlalchemy.url"],
229
+ poolclass=pool.NullPool,
230
+ )
231
+
232
+ with connectable.connect() as connection:
233
+ do_run_migrations(connection)
234
+
235
+ connectable.dispose()
236
+
237
+
238
+ if context.is_offline_mode():
239
+ run_migrations_offline()
240
+ else:
241
+ run_migrations_online()
242
+ ''',
243
+
244
+ "migrations_script_py_mako": lambda: '''"""${message}
245
+
246
+ Revision ID: ${up_revision}
247
+ Revises: ${down_revision | comma,n}
248
+ Create Date: ${create_date}
249
+
250
+ """
251
+ from typing import Sequence, Union
252
+
253
+ from alembic import op
254
+ import sqlalchemy as sa
255
+ ${imports if imports else ""}
256
+
257
+ # revision identifiers, used by Alembic.
258
+ revision: str = ${repr(up_revision)}
259
+ down_revision: Union[str, None] = ${repr(down_revision)}
260
+ branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)}
261
+ depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)}
262
+
263
+
264
+ def upgrade() -> None:
265
+ ${upgrades if upgrades else "pass"}
266
+
267
+
268
+ def downgrade() -> None:
269
+ ${downgrades if downgrades else "pass"}
270
+ ''',
271
+
272
+ "migrations_versions_init_py": lambda: '''"""
273
+ 数据库迁移版本目录
274
+ """
275
+ ''',
276
+
277
+ "migrations_init_py": lambda: '''"""
278
+ 数据库迁移目录
279
+ """
280
+ ''',
281
+ }