数据库迁移)
用Tortoise-ORM Aerich实现数据库迁移自动化安装必要依赖pipinstalltortoise-orm aerich补充说明安装数据库驱动 pip install tortoise-orm[asyncodbc]Tortoise 当前支持以下数据库SQLite (using aiosqlite、PostgreSQL 9.4 (using asyncpg)、MySQL/MariaDB (using asyncmy)、Microsoft SQL Server/Oracle (using asyncodbc)数据模型定义创建models/base.py文件代码如下fromtortoiseimportfields,modelsclassBaseModel(models.Model):idfields.BigIntField(pkTrue,indexTrue)classMeta:abstractTrueclassOperatorMixin:create_byfields.BigIntField(indexTrue,description创建人ID,nullTrue)create_timefields.DatetimeField(auto_now_addTrue,indexTrue,nullTrue,description创建时间)update_byfields.BigIntField(indexTrue,description修改人ID,nullTrue)update_timefields.DatetimeField(auto_nowTrue,indexTrue,nullTrue,description更新时间)创建models/admin.py文件代码如下fromtortoiseimportfieldsfrom.baseimportBaseModel,OperatorMixinfromschemas.menusimportMenuTypefrom.enumsimportMethodTypeclassUser(BaseModel,OperatorMixin):accountfields.CharField(description账号,max_length64,uniqueTrue)usernamefields.CharField(description用户名称,max_length64,nullTrue)genderfields.SmallIntField(default1,description性别1-男, 2-女, 0-保密)passwordfields.CharField(max_length128,description密码)avatarfields.CharField(indexTrue,description用户头像,max_length255,nullTrue)mobilefields.CharField(indexTrue,description联系方式,max_length20,nullTrue)emailfields.CharField(indexTrue,max_length255,description邮箱)statusfields.SmallIntField(default1,description状态1-正常, 0-禁用)is_superuserfields.BooleanField(defaultFalse,description是否为超级管理员)last_loginfields.DatetimeField(nullTrue,description最后登录时间,indexTrue)is_deletedfields.SmallIntField(default0,description逻辑删除标识0-未删除, 1-已删除)deptfields.ForeignKeyField(models.Dept,related_nameusers,indexTrue,nullTrue,on_deletefields.SET_NULL,description部门ID)rolesfields.ManyToManyField(models.Role,related_nameusers,throughsys_user_role,forward_keyuser_id,backward_keyrole_id)classMeta:tablesys_usertable_description用户表classDept(BaseModel,OperatorMixin):namefields.CharField(max_length100,uniqueTrue,description部门名称,indexTrue)codefields.CharField(max_length100,uniqueTrue,description部门编号,indexTrue)parent_idfields.IntField(default0,description父节点ID,indexTrue)tree_pathfields.CharField(max_length255,description父节点ID路径,indexTrue)sortfields.SmallIntField(default0,description显示顺序)statusfields.SmallIntField(default1,description状态1-正常 0-禁用)is_deletedfields.SmallIntField(default0,description逻辑删除标识1-已删除 0-未删除)classMeta:tablesys_depttable_description部门表classRole(BaseModel,OperatorMixin):namefields.CharField(description角色名称,max_length64,uniqueTrue)codefields.CharField(description角色编码,max_length32,uniqueTrue)sortfields.IntField(description显示顺序,indexTrue,nullTrue)statusfields.SmallIntField(default1,description角色状态1-正常, 0-停用)data_scopefields.SmallIntField(indexTrue,description数据权限1-所有数据, 2-部门及子部门, 3-本部门, 4-本人,nullTrue)descfields.CharField(max_length500,nullTrue,description角色描述)# 多对多关系menusfields.ManyToManyField(models.Menu,related_nameroles,throughsys_role_menu,forward_keyrole_id,backward_keymenu_id)apisfields.ManyToManyField(models.Api,related_nameapis,throughsys_role_api,forward_keyrole_id,backward_keyapi_id)is_deletedfields.SmallIntField(default0,description逻辑删除标识0-未删除, 1-已删除)classMeta:tablesys_roletable_description角色表classMenu(BaseModel,OperatorMixin):parent_idfields.BigIntField(description父菜单ID)tree_pathfields.CharField(max_length255,description父节点ID路径,nullTrue,indexTrue)titlefields.CharField(max_length64,description菜单名称)typefields.CharEnumField(MenuType,nullTrue,description菜单类型C-目录 M-菜单 B-按钮)namefields.CharField(max_length255,description路由名称Vue Router 中用于命名路由,nullTrue,indexTrue)pathfields.CharField(max_length128,description路由路径Vue Router 中定义的 URL 路径,nullTrue,indexTrue)componentfields.CharField(max_length128,description组件路径组件页面完整路径相对于 src/views/缺省后缀 .vue,nullTrue,indexTrue)permfields.CharField(max_length128,description权限标识按钮专用,nullTrue,indexTrue)always_showfields.SmallIntField(default0,description【目录】仅一个子路由时是否始终显示1-是, 0-否)keep_alivefields.SmallIntField(default0,description【菜单】是否开启页面缓存1-是, 0-否)visiblefields.SmallIntField(default1,description显示状态1-显示, 0-隐藏)sortfields.IntField(default0,description排序)iconfields.CharField(max_length64,description菜单图标,nullTrue,indexTrue)redirectfields.CharField(max_length128,description跳转路径,nullTrue,indexTrue)paramsfields.CharField(max_length255,description路由参数,nullTrue,indexTrue)classMeta:tablesys_menutable_description菜单表classApi(BaseModel,OperatorMixin):pathfields.CharField(max_length100,descriptionAPI路径,indexTrue)methodfields.CharEnumField(MethodType,description请求方法,indexTrue)summaryfields.CharField(max_length500,description请求简介,indexTrue)tagsfields.CharField(max_length100,descriptionAPI标签,indexTrue)classMeta:tablesys_api创建models/enums.py文件代码如下fromenumimportStrEnumclassMethodType(StrEnum):GETGETPOSTPOSTPUTPUTDELETEDELETEPATCHPATCH创建schemas/menus.py文件代码如下fromenumimportStrEnumclassMenuType(StrEnum):CATALOGcatalog# 目录MENUmenu# 菜单BUTTONbutton,# 按钮EXTLINKextlink,# 外链创建models/__init__.py文件代码如下from.adminimport*代码解析ForeignKeyField定义外键deptfields.ForeignKeyField(models.Dept,related_nameusers,indexTrue,nullTrue,on_deletefields.SET_NULL,description部门ID)model_name必填项写明要关联的模型名称支持字符串形式以便延迟加载 。related_name用于在关联模型上创建反向关系方便从另一方查询当前模型的数据 。on_delete定义当被关联的数据被删除时的处理策略默认是级联删除 。CASCADE级联删除关联模型被删当前模型对应数据也跟着删 。SET_NULL置为空关联模型被删外键字段变为 NULL需设置 nullTrue 。RESTRICT限制删除只要有外键指向就不允许删除关联模型 。SET_DEFAULT重置为默认值关联模型被删外键字段变为默认值需设置 default 。db_constraint控制是否在数据库层面创建外键约束默认开启以保证数据完整性 。ManyToManyField定义两个模型之间的多对多关系rolesfields.ManyToManyField(models.Role,related_nameusers,throughsys_user_role,forward_keyuser_id,backward_keyrole_id)related_model位置参数目标模型的字符串路径如 “models.Role”必须指定。related_name反向关系名用于从目标模型访问此关系如 related_name“users”。through自定义中间表名字符串如 “sys_user_role”不指定时自动创建。forward_key指定中间表里指向“定义该字段的模型”的外键列名默认自动生成为 {model_name}_id小写例如若模型是 User则默认 forward_key“user_id”。backward_key 对应中间表中指向“目标模型”的外键列默认为 {target_model_name}_id。**CharEnumField **用于数据库字段的字符串枚举类型typefields.CharEnumField(MenuType,nullTrue,description菜单类型C-目录 M-菜单 B-按钮)enum_type枚举类description描述。若未指定将自动设置为包含“名称: 值”对的多行列表。max_length长度。若为零则会从enum_type自动检测。注册模型修改settings/config.py文件添加如下代码propertydefTORTOISE_ORM(self)-dict:# SQLite fallback configurationreturn{connections:{default:{engine:tortoise.backends.sqlite,credentials:{file_path:fastapi_backend.sqlite3},}},apps:{models:{models:[models,aerich.models],default_connection:default,},},use_tz:False,timezone:Asia/Shanghai,}修改core/init_app.py文件代码如下fromfastapiimportFastAPIfromapiimportapi_routerfromaerichimportCommandfromlogimportloggerfromsettings.configimportsettingsdefregister_routers(app:FastAPI,prefix:str/api):app.include_router(api_router,prefixprefix)asyncdefinit_db():commandCommand(tortoise_configsettings.TORTOISE_ORM)try:awaitcommand.init_db(safeTrue)exceptFileExistsError:passawaitcommand.init()try:awaitcommand.migrate(no_inputTrue)exceptAttributeErrorase:logger.error(f数据库迁移失败:{e})logger.warning(请手动检查数据库和migrations状态)raiseRuntimeError(数据库迁移失败请检查数据库连接和migrations状态)fromeawaitcommand.upgrade(run_in_transactionTrue)asyncdefinit_data():logger.info( 系统初始化开始...)logger.info( 开始数据库初始化和迁移...)awaitinit_db()logger.info(✅ 数据库初始化完成)修改main.py文件代码如下fromfastapiimportFastAPIfromcontextlibimportasynccontextmanagerfromtortoiseimportTortoisefromcore.init_appimportregister_routers,init_dataasynccontextmanagerasyncdeflifespan(app:FastAPI):awaitinit_data()yieldawaitTortoise.close_connections()appFastAPI(lifespanlifespan)register_routers(app,prefix/api)执行uvicorn main:app运行项目项目启动之后会自动生成迁移文件文件在migrations/目录下面 和 数据库文件fastapi_backend.sqlite3连接数据库之后可以看到如下图所示的表格其中sys_role_api、sys_role_menu和sys_user_role是通过ManyToManyField定义的多对多关系自动生成的。