Schema 生成
本指南涵盖 schema 生成的机制。你将学会如何:
- 理解 Lucid 何时自动重新生成
database/schema.ts - 手动运行
schema:generate以及何时使用它 - 采用没有迁移历史的现有数据库
- 从生成中排除表和 PostgreSQL schemas
- 决定是否提交生成的文件
- 当生成不适合你的工作流时禁用它
概述
Lucid 采用数据库优先的方法:你在迁移中描述 schema,Lucid 通过内省生成的表来生成带类型的 database/schema.ts 文件。你的模型继承生成的类并自动继承列类型。参见简介了解此设计背后的理念。
本页专注于生成管道。有关从模型中使用生成的类——列类型映射、模型级覆盖以及使用 schema 规则自定义类型——请参见 schema 类指南。
何时运行生成
Lucid 在每个更改数据库 schema 的命令之后自动重新生成 database/schema.ts:
migration:runmigration:rollbackmigration:refreshmigration:resetmigration:fresh
这意味着模型无需手动干预即可与数据库保持同步。迁移应用后,schema 文件反映新的结构,你的模型在 TypeScript 下次编译时会获取这些变更。
在这些命令上传递 --no-schema-generate 以跳过重新生成。当你想要应用迁移而不修改 schema 文件时使用此选项(例如,当你有意手动编辑文件或提交自定义版本时)。
node ace migration:run --no-schema-generate
有关每个命令的完整标志列表,请参见命令参考。
生成的文件
默认情况下,生成的文件位于 database/schema.ts。数据库中的每个表都成为一个 schema 类,通过将表名转换为 PascalCase 并附加 Schema 来命名。
import { BaseModel, column } from '@adonisjs/lucid/orm'
import { DateTime } from 'luxon'
export class UsersSchema extends BaseModel {
static table = 'users'
@column({ isPrimary: true })
declare id: number
@column()
declare email: string
@column()
declare password: string
@column.dateTime({ autoCreate: true })
declare createdAt: DateTime
@column.dateTime({ autoCreate: true, autoUpdate: true })
declare updatedAt: DateTime
}
export class PostsSchema extends BaseModel {
static table = 'posts'
@column({ isPrimary: true })
declare id: number
// ...
}
几个值得了解的约定:
- 表名在数据库中是复数形式,在生成的文件中变为 PascalCase +
Schema(users→UsersSchema,blog_posts→BlogPostsSchema)。 - 列名在模型属性上从 snake_case 转换为 camelCase(
created_at→createdAt)。 static table属性记录原始数据库表名,以便模型在查询时解析回正确的表。- 文件在每次生成时都会被覆盖,因此对
database/schema.ts的任何手动编辑都会丢失。通过 schema 规则或模型级覆盖来自定义。
手动运行 schema
schema:generate Ace 命令按需重新生成文件。在三种情况下使用它:
- 采用现有数据库。 当你将 Lucid 指向已经有表的数据库(而非运行迁移)时,
schema:generate生成你开始构建模型所需的 schema 类。 - 手动 schema 变更之后。 如果同事通过 psql 直接应用了 schema 变更,或者队友在共享开发数据库上运行了迁移,运行
schema:generate将变更拉取到你的本地 schema 文件中。 - 恢复缺失的 schema 文件。 如果
database/schema.ts被删除或变得过时(例如 Git 合并冲突),从当前数据库状态重新生成它。
node ace schema:generate
参见命令参考了解完整标志列表(--connection、--compact-output)。
采用现有数据库
Schema 生成使 Lucid 可用于不是由 Lucid 迁移创建的数据库。当你将现有数据库纳入 Lucid 模型管理时,请遵循此工作流。
1. 配置数据库连接。 使用现有数据库的连接详细信息设置 config/database.ts。参见配置指南了解驱动特定选项。
import env from '#start/env'
import { defineConfig } from '@adonisjs/lucid'
const dbConfig = defineConfig({
connection: 'postgres',
connections: {
postgres: {
client: 'pg',
connection: env.get('DATABASE_URL'),
},
},
})
export default dbConfig
2. 决定你要建模哪些表。 大多数遗留数据库包含你的应用不应通过 Lucid 触及的表:来自之前工具的框架迁移表、审计日志、队列或缓存表等。将它们列在 schemaGeneration.excludeTables 下,以便从生成的文件中跳过它们。
{
postgres: {
client: 'pg',
connection: env.get('DATABASE_URL'),
schemaGeneration: {
excludeTables: [
'knex_migrations',
'knex_migrations_lock',
'pgboss_jobs',
'audit_log',
],
},
},
}
3. 生成 schema 文件。 运行 schema:generate 来内省数据库并写入 database/schema.ts。
node ace schema:generate
打开生成的文件并审查类。你未排除的每个表都显示为一个带有列声明的 *Schema 类。
4. 为你想要使用的表创建模型。 你不需要为每个表都创建模型——只需要你的应用与之交互的那些。为每个表生成一个继承相应 schema 类的模型。
node ace make:model User
node ace make:model Post
import { UsersSchema } from '#database/schema'
export default class User extends UsersSchema {}
5. 决定 schema 将如何演进。 从这里有两条路径:
- 继续在 Lucid 之外管理 schema。 继续通过你现有的工具(原始 SQL、另一个服务中的 ORM、DBA 工作流)应用 schema 变更。每当 schema 变更时运行
node ace schema:generate以刷新database/schema.ts。 - 将 schema 管理迁移到 Lucid 迁移中。 将所有未来的 schema 变更编写为 Lucid 迁移。第一次
migration:run将应用你的新迁移并使用结果重新生成 schema 文件。历史 schema 不受影响,因为 Lucid 不需要现有的迁移历史即可工作。
这两条路径也可以混合使用:为在其他地方管理的表手写 schema,为通过 Lucid 添加的新表编写迁移。
6. 处理约定不匹配。 不遵循 Lucid 默认值的表和列(snake_case 列、复数表名、单列主键、整数或 UUID 标识符)需要 schema 规则或模型级覆盖。参见使用 schema 规则自定义类型和模型级覆盖了解相关模式。
自定义生成的输出
schemaGeneration 配置块接受一个 rulesPaths 数组,指向自定义列和表输出方式的 TypeScript 模块。规则可以覆盖特定列的 TypeScript 类型、更改推断的主键、自定义命名等。
{
schemaGeneration: {
enabled: true,
outputPath: 'database/schema.ts',
rulesPaths: ['database/schema_rules.ts'],
},
}
export default {
// ... 规则定义
}
规则格式和类型自定义模式在 schema 类指南中有详细介绍,因为这些选择从根本上关系到你的模型继承的类型。
排除表和 schemas
两个配置选项控制 Lucid 在生成期间考虑哪些表。
excludeTables 按名称跳过特定表。用于你的应用不应通过 Lucid 访问的表(框架元数据、在其他地方维护的审计表、由不同工具管理的队列表)。
schemaGeneration: {
excludeTables: ['knex_migrations', 'knex_migrations_lock', 'pgboss_jobs'],
}
schemas 将生成限制在特定的 PostgreSQL schemas。没有它,生成器会扫描连接有权访问的每个 schema。
schemaGeneration: {
schemas: ['public', 'app'],
}
完整的 schemaGeneration 配置参考在配置指南中。
版本控制
将 database/schema.ts 提交到你的仓库。即使该文件会被重新生成,将其视为受版本控制的有几个好处:
- 无需构建步骤的 IDE 智能提示。 编辑器在文件出现在磁盘上的那一刻就能获取带类型的 schema 类。不提交的话,每个开发者都必须运行
schema:generate(或应用迁移)才能编写模型。 - Schema 变更可在 Pull Request 中审查。 添加列的迁移会在
database/schema.ts中产生相应的 diff。审查者可以在迁移代码旁边看到 schema 影响,这能捕获仅看迁移时隐藏的错误。 - 确定性 CI。 CI 工作流可以立即进行类型检查,无需先运行迁移。生产构建也是如此。
- 漂移检测。 当提交的 schema 和重新生成的 schema 不一致时,你就知道有人在迁移管道之外更改了数据库。添加一个运行
schema:generate并在产生 diff 时失败的 CI 步骤来自动捕获这种情况。
将 database/schema.ts 作为普通文件添加到你的仓库。文件的自动生成头清楚地表明该文件不应手动编辑。
禁用生成
将 schemaGeneration.enabled 设置为 false 以同时禁用迁移后的自动重新生成和 schema:generate 命令。这很少见;仅当你完全手动维护 database/schema.ts 或你的项目根本不使用 Lucid 模型时才使用。
schemaGeneration: {
enabled: false,
}
当生成被禁用时,schema 相关命令会报告为空操作。只要有手动维护的 database/schema.ts 存在,模型仍然可以工作;没有它,你的模型没有 schema 类可以继承。