Schema 类
本指南涵盖模型如何使用 Lucid 从数据库生成的 schema 类。你将学会如何:
- 阅读和理解生成的 schema 类
- 了解每种数据库列产生的 TypeScript 类型
- 处理默认映射需要特别注意的常见类型(枚举、JSON、日期、主键)
- 使用 schema 规则全局或按表自定义生成的类型
- 使用特定类型、访问器和读/写钩子在模型类上覆盖列
有关生成工作流本身(schema:generate 何时运行、采用遗留数据库、提交生成的文件),请参见 schema 生成指南。有关迁移优先的理念,请参见简介。
生成的 schema 类剖析
当迁移运行时,Lucid 为每个表在 database/schema.ts 中写入一个类。每个类继承 BaseModel,声明 static table,暴露一个只读的列名 $columns 元组,并使用适当的 @column 装饰器声明每一列。
import { DateTime } from 'luxon'
import { BaseModel, column } from '@adonisjs/lucid/orm'
export class PostsSchema extends BaseModel {
static table = 'posts'
static $columns = ['id', 'title', 'content', 'createdAt', 'updatedAt'] as const
$columns = PostsSchema.$columns
@column({ isPrimary: true })
declare id: number
@column()
declare title: string
@column()
declare content: string
@column.dateTime({ autoCreate: true })
declare createdAt: DateTime | null
@column.dateTime({ autoCreate: true, autoUpdate: true })
declare updatedAt: DateTime | null
}
关于生成输出有几个值得了解的要点:
- 列名从 snake_case 转换为 camelCase。数据库中的
created_at在模型上变为createdAt,内部记录columnName: 'created_at',以便查询定位正确的列。 static $columns元组驱动自动补全。query().select(...)等方法使用此元组提供类型安全的列名补全。- 主键列从数据库检测并用
@column({ isPrimary: true })装饰。 - 日期和 datetime 列使用
@column.date和@column.dateTime装饰器,而非普通的@column。两者都返回 LuxonDateTime实例。
你永远不直接编辑 database/schema.ts。该文件在每次生成时被覆盖;通过 schema 规则或模型级覆盖来自定义。
你的模型继承生成的类并添加行为。
import { PostsSchema } from '#database/schema'
export default class Post extends PostsSchema {}
列类型参考
Lucid 通过内部映射将数据库特定的列类型转换为 TypeScript 类型。数据库驱动程序报告原生列类型(如 varchar 或 int4),Lucid 将其映射到内部类别(如 string 或 number),内部类别决定最终的 TypeScript 类型。
内部类型也是你使用 schema 规则自定义类型时定位的键。
内部类型
| 内部类型 | 默认 TypeScript 类型 | 描述 |
|---|---|---|
number | number | 整数和浮点列 |
bigint | bigint | number | 大整数。联合默认值涵盖了将 bigint 作为 JavaScript number(当值适合时)和 bigint(当不适合时)返回的驱动。如果你的驱动一致,使用 schema 规则覆盖。 |
decimal | string | 带精度的小数类型。默认为 string 以保留精确精度;当应用端精度损失可接受时覆盖为 number。 |
boolean | boolean | 布尔列 |
string | string | 所有文本和字符类型 |
date | DateTime | 不带时间的日期。使用 @column.date 装饰器。 |
time | string | 不带日期的时间 |
DateTime | DateTime | 组合日期和时间。使用 @column.dateTime 装饰器。 |
binary | Buffer | 二进制和 blob 列 |
json | any | JSON 列 |
jsonb | any | PostgreSQL JSONB 列 |
uuid | string | UUID 和 GUID 列 |
enum | string | 枚举类型 |
set | string | MySQL SET 类型 |
unknown | any | Lucid 不识别的类型。回退为 any 以便列可用而无需手动转换。 |
数据库类型到内部类型
下表列出 Lucid 识别的具体数据库类型及其映射方式。对于未列出的任何类型,Lucid 回退为 unknown。
数值类型
| 数据库类型 | 内部类型 | 备注 |
|---|---|---|
smallint、integer、int | number | 标准整数 |
bigint、unsigned big int | bigint | 大整数 |
decimal、numeric、money | decimal | 精确小数 |
real、double、double precision、float | number | 浮点数 |
tinyint | boolean | MySQL 布尔约定 |
mediumint | number | MySQL 中等整数 |
smallmoney | decimal | MSSQL 货币 |
smallserial、serial | number | PostgreSQL 自增 |
bigserial | bigint | PostgreSQL 大自增 |
int2、int4 | number | PostgreSQL 整数别名 |
int8 | bigint | PostgreSQL bigint 别名 |
float4、float8 | number | PostgreSQL 浮点别名 |
oid | number | PostgreSQL 对象标识符 |
year | number | MySQL 年份类型 |
布尔类型
| 数据库类型 | 内部类型 |
|---|---|
boolean、bool | boolean |
bit(MSSQL) | boolean |
文本类型
| 数据库类型 | 内部类型 | 备注 |
|---|---|---|
char、varchar、text | string | 标准文本 |
character、character varying | string | PostgreSQL 变体 |
tinytext、mediumtext、longtext | string | MySQL 文本大小 |
nchar、nvarchar、clob | string | 其他方言 |
ntext、sysname | string | MSSQL 文本 |
xml | string | 存储为文本的 XML |
日期和时间类型
| 数据库类型 | 内部类型 | 备注 |
|---|---|---|
date | date | 仅日期 |
time | time | 仅时间,映射为 string |
time without time zone、time with time zone | time | PostgreSQL 时间变体 |
datetime、timestamp | DateTime | 组合日期和时间 |
timestamp without time zone | DateTime | PostgreSQL |
timestamp with time zone | DateTime | 带时区的 PostgreSQL |
smalldatetime、datetime2 | DateTime | MSSQL 变体 |
datetimeoffset | DateTime | 带时区的 MSSQL |
interval | string | PostgreSQL 间隔存储为字符串 |
二进制类型
| 数据库类型 | 内部类型 |
|---|---|
bytea、blob | binary |
tinyblob、mediumblob、longblob | binary |
binary、varbinary | binary |
image、rowversion | binary |
JSON 类型
| 数据库类型 | 内部类型 |
|---|---|
json | json |
jsonb | jsonb |
UUID 类型
| 数据库类型 | 内部类型 |
|---|---|
uuid | uuid |
uniqueidentifier | uuid |
枚举和集合类型
| 数据库类型 | 内部类型 |
|---|---|
enum | enum |
set | set |
PostgreSQL 特殊类型
| 数据库类型 | 内部类型 | 备注 |
|---|---|---|
inet、cidr、macaddr、macaddr8 | string | 网络地址 |
tsvector、tsquery | string | 全文搜索 |
bit、bit varying | string | 位串 |
hstore | unknown | 键值存储 |
int4range、int8range、numrange、tsrange、tstzrange、daterange | unknown | 范围类型 |
int4multirange、int8multirange、nummultirange、tsmultirange、tstzmultirange、datemultirange | unknown | 多范围类型(PostgreSQL 14+) |
pg_lsn、name、pg_snapshot、txid_snapshot | string | 特殊标识符类型 |
regclass、regproc、regprocedure、regoper、regoperator、regtype、regrole、regnamespace | string | 对象标识符类型 |
point | string | 点几何 |
line、lseg、box、path、polygon、circle | string | 几何形状 |
multipoint、multilinestring、multipolygon、geometrycollection | string | 几何集合 |
geometry、geography | unknown | 未类型化空间 |
MSSQL 特殊类型
| 数据库类型 | 内部类型 |
|---|---|
hierarchyid | string |
sql_variant | unknown |
SQLite 类型亲和变体
SQLite 以大写形式返回类型,与标准小写变体一起映射:
| 数据库类型 | 内部类型 |
|---|---|
INTEGER | number |
REAL | number |
TEXT | string |
BLOB | binary |
NUMERIC | number |
有关包括方言特定差异的完整最新映射,请参见 GitHub 上的映射源码。
方言限定类型查找
在查询通用映射之前,生成器首先查找 {dialect}.{columnType}。这使一个类型在不同方言中可以表示不同含义。例如,bit 在 PostgreSQL 上是位串(映射为 string),但 mssql.bit 是 SQL Server 上的 1 位布尔(映射为 boolean)。用户很少直接与此交互;只有当你定义需要定位特定方言对类型的解释的 schema 规则时才重要。
可空性和列顺序
两个约定应用于每个生成的类:
- 可空性。 当数据库列可为空且不是主键时,Lucid 在 TypeScript 类型后追加
| null。主键永远不会收到| null后缀,无论其数据库可空性如何。 - 列顺序。 列在生成的类中按属性名按字母顺序排序。顺序不反映表的物理列顺序。
不是有效 JavaScript 的标识符
当列名以不是有效 JavaScript 标识符的字符开头时(例如数字如 2fa_secret),生成器在属性名前加下划线(_2fa_secret),并在装饰器的 columnName 参数中记录原始列名,以便查询仍然定位正确的列。
@column({ columnName: '2fa_secret' })
declare _2fa_secret: string | null
使用常见数据类型
大多数类型无需额外配置即可工作。以下类型需要说明,因为它们要么需要设计决策(枚举、JSON),要么产生更丰富的运行时对象(日期、主键)。
枚举
数据库原生枚举类型通常不适合应用数据,原因有三:
- SQLite 不支持枚举。基于枚举的迁移在方言之间不可移植。
- PostgreSQL 将枚举存储为独立类型,增加了迁移开销。
- 在生产中添加、移除或重命名枚举值通常需要表重写或手动 SQL,某些方言完全禁止其中一些操作。
Lucid 默认将数据库枚举列映射为 TypeScript string。推荐的模式是将值存储为整数(或短字符串),在应用代码中定义语义。
import { BaseSchema } from '@adonisjs/lucid/schema'
export default class extends BaseSchema {
async up() {
this.schema.createTable('posts', (table) => {
table.increments('id')
table.string('title').notNullable()
table.integer('status').unsigned().notNullable().defaultTo(0)
table.timestamps(true, true)
})
}
}
import { PostsSchema } from '#database/schema'
export const PostStatus = {
DRAFT: 0,
PUBLISHED: 1,
ARCHIVED: 2,
} as const
export type PostStatus = (typeof PostStatus)[keyof typeof PostStatus]
export default class Post extends PostsSchema {
get isDraft() {
return this.status === PostStatus.DRAFT
}
get isPublished() {
return this.status === PostStatus.PUBLISHED
}
}
PostStatus 同时用作运行时值对象和 TypeScript 类型。你可以在同一文件中将其用作任一种。
如果你仍然偏好原生枚举,使用 schema 规则自定义生成的列类型以产生严格的联合类型。
JSON 列
JSON 和 JSONB 列默认映射为 TypeScript any。你获得最大灵活性,但牺牲了类型安全。
@column()
declare metadata: any
两条路径可以收紧此类型。
第一条是应用于你应用中每个 JSON 列的 schema 规则。
import { type SchemaRules } from '@adonisjs/lucid/types/schema_generator'
export default {
types: {
json: {
tsType: 'JSON<any>',
decorators: [{ name: '@column' }],
imports: [
{ source: '#types/db', typeImports: ['JSON'] },
],
},
},
tables: {},
} satisfies SchemaRules
export type JSON<T> = T
第二条是针对一个特定列的模型级覆盖。
import type { JSON } from '#types/db'
import { column } from '@adonisjs/lucid/orm'
import { PostsSchema } from '#database/schema'
export default class Post extends PostsSchema {
@column()
declare metadata: JSON<{ seoTitle?: string; keywords?: string[] }>
}
两种方法可以一起使用。参见下面的组合全局和表规则。
PostgreSQL 中 json 和 jsonb 的区别在于运行时:jsonb 可索引且查询稍快,而 json 保留精确的输入格式。两者在 TypeScript 中默认都映射为 any,规则将它们作为单独的内部类型(json vs jsonb)定位,因此你可以相同或不同地对待它们。
日期和时间戳
每个日期和时间戳列都映射为 Luxon DateTime 实例,因此日期拥有完整的 API 可用,而非原始字符串。
import { PostsSchema } from '#database/schema'
const post = await Post.findOrFail(params.id)
post.createdAt.toFormat('yyyy-MM-dd')
const daysSinceCreated = DateTime.now().diff(post.createdAt, 'days').days
if (post.publishedOn && post.publishedOn > DateTime.now()) {
// 预定稍后发布
}
生成的 schema 类对日期列使用 @column.date,对 datetime 和 timestamp 列使用 @column.dateTime。两个装饰器在当前版本中功能相同并接受相同的选项。
使用 table.timestamps(true, true) 创建的时间戳列标注了 autoCreate 和 autoUpdate,它们告诉 Lucid 分别在插入和更新时自动设置它们。
@column.dateTime({ autoCreate: true })
declare createdAt: DateTime | null
@column.dateTime({ autoCreate: true, autoUpdate: true })
declare updatedAt: DateTime | null
主键
Lucid 在生成 schema 类时从数据库检测主键列。无论其名称如何,该列都用 @column({ isPrimary: true }) 装饰,其类型匹配数据库列的类型(整数、字符串、UUID)。
import { BaseSchema } from '@adonisjs/lucid/schema'
export default class extends BaseSchema {
async up() {
this.schema.createTable('oauth_states', (table) => {
table.string('key').notNullable().primary()
table.text('value').notNullable()
table.timestamp('updated_at')
})
}
}
export class OauthStatesSchema extends BaseModel {
static table = 'oauth_states'
@column({ isPrimary: true })
declare key: string
@column()
declare value: string
@column.dateTime({ autoCreate: true, autoUpdate: true })
declare updatedAt: DateTime | null
}
当你的主键不叫 id 时,也在你的应用模型上设置 static primaryKey,以便关联关系和默认查找器使用正确的列。
import { OauthStatesSchema } from '#database/schema'
export default class OauthState extends OauthStatesSchema {
static primaryKey = 'key'
}
Lucid 模型仅支持单一主键。当表有复合主键时,Lucid 使用第一列。要自定义决策,使用自定义主键检测规则。
使用 schema 规则自定义类型
Schema 规则允许你更改 Lucid 生成类型和装饰器的方式,而无需手动编辑 database/schema.ts。规则存放在数据库配置中 schemaGeneration.rulesPaths 指向的文件中(通常是 database/schema_rules.ts),由生成器自动加载。参见配置指南了解字段参考。
一个初始规则文件如下所示:
import { type SchemaRules } from '@adonisjs/lucid/types/schema_generator'
export default {
types: {},
columns: {},
tables: {},
} satisfies SchemaRules
规则文件的结构
规则文件接受五个顶级键,其中任何一个都可以省略。
-
types
-
按内部类型名键控的规则(
number、bigint、decimal、string、json、jsonb、uuid、enum、set以及内部类型表中的其他类型)。规则适用于每个表中映射到该内部类型的每一列。 -
columns
-
按数据库列名键控的规则。规则适用于每个表中具有该确切名称的每一列。Lucid 的内置默认值使用此功能将
autoCreate应用于每个created_at列,将autoCreate + autoUpdate应用于每个updated_at列。 -
tables
-
按表规则。每个表条目可以定义自己的
types、columns、skipColumns和primaryKey,仅适用于该表。 -
primaryKey
-
一个函数,决定每个表的主键如何表示。接收表名、检测到的主键列名和完整的列元数据。参见自定义主键检测。
规则解析优先级
对于每一列,生成器按以下顺序执行五步查找并使用第一个匹配:
- 表特定列规则,位于
tables[tableName].columns[columnName] - 表特定类型规则,位于
tables[tableName].types[internalType] - 全局列规则,位于
columns[columnName] - 主键规则结果,当该列是检测到的主键时
- 全局类型规则,位于
types[internalType]
更具体的规则优先于不太具体的规则。表列规则覆盖全局列规则,全局列规则覆盖全局类型规则。
规则在生成 schema 文件时应用。更改规则后,运行 node ace schema:generate 重新生成,或等待下一个迁移命令自动重新生成。
规则值的结构
每个规则返回一个带有三个字段的 ColumnInfo 对象:
type ColumnInfo = {
tsType: string
decorators?: { name: string; args?: Record<string, any> }[]
imports?: ImportInfo[]
}
-
tsType
-
在列上声明的 TypeScript 类型,作为字符串(
'number'、'string'、'JSON<any>'、`'admin' | 'user'`)。 -
decorators
-
在列上方发出的装饰器。
{ name, args? }对象的数组,其中name是以@column开头的装饰器名(例如'@column'、'@column.date'、'@column.dateTime'),args是装饰器参数的可选对象。 -
imports
-
生成的文件需要的导入,以便装饰器和类型可以解析。每个条目是一个
ImportInfo对象:{ source, namedImports?, typeImports?, defaultImport?, defaultTypeImport? }。对运行时值(如 Luxon 的DateTime)使用namedImports,对仅类型导入使用typeImports。
规则也可以返回 ColumnInfo 的函数。函数接收数据库类型字符串('varchar'、'int4' 等)并返回规则。当同一内部类型需要根据具体数据库类型略有不同的处理时使用此方式。
types: {
string: (dataType) => ({
tsType: dataType === 'text' ? 'string' : `string`,
decorators: [{ name: '@column' }],
imports: [],
}),
}
全局类型规则
全局类型规则定位映射到给定内部类型的每一列。
import { type SchemaRules } from '@adonisjs/lucid/types/schema_generator'
export default {
types: {
json: {
tsType: 'JSON<any>',
decorators: [{ name: '@column' }],
imports: [
{ source: '#types/db', typeImports: ['JSON'] },
],
},
bigint: {
tsType: 'bigint',
decorators: [{ name: '@column' }],
},
},
tables: {},
} satisfies SchemaRules
使用这些规则,每个 JSON 列变为 JSON<any>,每个 bigint 列变为 TypeScript bigint。
全局列规则
全局列规则定位每个表中具有给定名称的每一列。Lucid 附带两个内置全局列规则,默认继承:
created_at发出@column.dateTime({ autoCreate: true })updated_at发出@column.dateTime({ autoCreate: true, autoUpdate: true })
为你一致使用的列添加自己的规则。例如,如果数据库中的每个加密列都命名为 ciphertext_*,全局规则保持它们统一类型化:
export default {
columns: {
deleted_at: {
tsType: 'DateTime | null',
decorators: [{ name: '@column.dateTime' }],
imports: [
{ source: 'luxon', namedImports: ['DateTime'] },
],
},
},
} satisfies SchemaRules
表特定列规则
列规则定位一个特定表上的一个特定列。
export default {
tables: {
users: {
columns: {
role: {
tsType: `'admin' | 'moderator' | 'user'`,
decorators: [{ name: '@column' }],
},
},
},
},
} satisfies SchemaRules
使用此规则,users.role 生成为:
@column()
declare role: 'admin' | 'moderator' | 'user'
表特定类型规则
按表类型规则为一个表覆盖全局类型规则。当一个表与应用其余部分对类型的处理方式不同时使用此方式。
export default {
types: {
json: {
tsType: 'JSON<any>',
decorators: [{ name: '@column' }],
imports: [
{ source: '#types/db', typeImports: ['JSON'] },
],
},
},
tables: {
audit_events: {
types: {
json: {
tsType: 'Record<string, unknown>',
decorators: [{ name: '@column' }],
},
},
},
},
} satisfies SchemaRules
应用中的 JSON 列类型化为 JSON<any>,但 audit_events 内的 JSON 列类型化为 Record<string, unknown>。
跳过列
要从生成的 schema 类中排除特定列(例如,由数据库触发器或单独工具管理的列),在表的 skipColumns 中列出它们。
export default {
tables: {
users: {
skipColumns: ['search_tsv', 'ltree_path'],
},
},
} satisfies SchemaRules
这些列完全从生成的类中排除。通过模型的查询仍会命中该表,但这些列对 TypeScript 和 Lucid 的变更跟踪不可见。
组合规则
规则从最不具体到最具体组合。列规则细化类型规则,按表规则细化全局规则,装饰器参数累积。
export default {
types: {
json: {
tsType: 'JSON<any>',
decorators: [{ name: '@column' }],
imports: [
{ source: '#types/db', typeImports: ['JSON'] },
],
},
},
columns: {
deleted_at: {
tsType: 'DateTime | null',
decorators: [{ name: '@column.dateTime' }],
imports: [
{ source: 'luxon', namedImports: ['DateTime'] },
],
},
},
tables: {
posts: {
columns: {
metadata: {
tsType: 'JSON<{ title?: string; description?: string; ogImage?: string }>',
decorators: [{ name: '@column' }],
imports: [
{ source: '#types/db', typeImports: ['JSON'] },
],
},
},
},
},
} satisfies SchemaRules
应用中的 JSON 列变为 JSON<any>,除了 posts.metadata 使用其特定形状。每个 deleted_at 列都变为可空的 DateTime,无论它出现在哪个表上。
自定义主键检测
默认情况下,Lucid 使用数据库返回的第一个主键列并应用 @column({ isPrimary: true }),TypeScript 类型从列的数据库类型推断(因此 integer 主键变为 number,uuid 主键变为 string)。使用 primaryKey 函数覆盖此行为,可以全局或按表。
回调具有以下签名:
type PrimaryKeyRule = (
tableName: string,
primaryKeys: string[],
columns: Record<string, DatabaseColumn>,
) => { columnName: string; columnInfo: ColumnInfo } | undefined
columns 是从列名到列元数据的映射。每个条目具有以下形状:
type DatabaseColumn = {
type: string
nullable: boolean
defaultValue?: any
maxLength?: number | null
}
返回 undefined 让 Lucid 对该表回退到默认检测。
import { type SchemaRules } from '@adonisjs/lucid/types/schema_generator'
export default {
primaryKey: (tableName, primaryKeys, columns) => {
const columnName = primaryKeys[0]
if (!columnName || !columns[columnName]) {
return undefined
}
return {
columnName,
columnInfo: {
tsType: 'string',
decorators: [{ name: '@column', args: { isPrimary: true } }],
imports: [],
},
}
},
} satisfies SchemaRules
表级 primaryKey 规则仅覆盖该表的全局规则。
export default {
tables: {
oauth_states: {
primaryKey: (tableName, primaryKeys, columns) => {
const columnName = primaryKeys[0]
if (!columnName || !columns[columnName]) {
return undefined
}
return {
columnName,
columnInfo: {
tsType: 'string',
decorators: [{ name: '@column', args: { isPrimary: true } }],
imports: [],
},
}
},
},
},
} satisfies SchemaRules
如果你为恰好是主键的列定义了列规则(在 columns 下),列规则获胜,primaryKey 规则对该列被跳过。
在模型上覆盖列
Schema 规则自定义生成器发出的内容。模型级覆盖允许单个模型在 TypeScript 级别细化或重新声明列,在模型文件本身中。
在以下情况下使用模型覆盖:
- 只有一个模型需要类型细化,schema 规则过于大材小用。
- 你想要列上的自定义读或写行为(通过
consume、prepare或 getter 和 setter),这些不属于生成的文件。 - 你想要向装饰器添加额外选项(
columnName、meta)而不编辑 schema 规则。
通过在模型上使用兼容类型重新声明列来覆盖。
import type { JSON } from '#types/db'
import { column } from '@adonisjs/lucid/orm'
import { PostsSchema } from '#database/schema'
export default class Post extends PostsSchema {
@column()
declare metadata: JSON<Partial<{
title: string
description: string
ogImage: string
keywords: string[]
}>>
}
模型继承 schema 类的列定义,除非你覆盖它,重新声明的属性遮蔽父类的属性。
@column 选项参考
@column 装饰器接受具有以下字段的 options 对象。这些适用于普通的 @column() 装饰器和日期变体(@column.date()、@column.dateTime())。
-
columnName
-
当无法从属性名派生时的实际数据库列名。在生成的类中很少需要(生成器已经记录了此信息),但在模型覆盖中有用,当你想重命名属性而不更改数据库时。
@column({ columnName: 'email_address' })declare email: string -
isPrimary
-
将列标记为主键。由生成器自动为检测到的主键设置。仅在生成器无法确定主键时才在覆盖上显式声明。
-
consume
-
从数据库读取行时调用的函数。将原始列值转换为模型使用的形状。常见情况包括驱动作为字符串返回的 JSON 列、你想要作为字符串的数值 ID,或需要解密的加密值。
@column({consume: (value) => typeof value === 'string' ? JSON.parse(value) : value,})declare preferences: Record<string, unknown>回调接收原始值、属性名和模型实例。
-
prepare
-
在值发送到数据库进行插入或更新之前调用的函数。
consume的逆操作。用于在持久化之前序列化、加密或规范化值。@column({prepare: (value) => typeof value === 'object' ? JSON.stringify(value) : value,})declare preferences: Record<string, unknown> -
meta
-
附加到列的任意元数据对象。Lucid 不使用此字段;用于你自己的工具(自定义装饰器、表单生成器、管理面板),这些工具需要每列的额外信息。
@column({ meta: { editableInAdmin: false } })declare internalId: string
日期列特定的选项
@column.date 和 @column.dateTime 装饰器接受上述每个 @column 选项,以及两个用于自动时间戳的额外字段。
-
autoCreate
-
当为
true时,Lucid 在插入新行时将列设置为当前DateTime。由生成器应用于created_at和updated_at列。@column.dateTime({ autoCreate: true })declare createdAt: DateTime | null -
autoUpdate
-
当为
true时,Lucid 在每次保存模型时将列设置为当前DateTime。与autoCreate配对用于updatedAt风格的列,或单独用于跟踪最近修改但不在创建时设置的字段。@column.dateTime({ autoCreate: true, autoUpdate: true })declare updatedAt: DateTime | null
列的自定义 getter 和 setter
当你需要超出 consume 和 prepare 所表达的自定义读或写行为时,在模型上为列添加 TypeScript getter 或 setter。Lucid 仍然通过 $attributes 读写底层属性,因此 getter 和 setter 包装该值而不替换 Lucid 的存储。
模式是:在 $attributes 下声明一个私有的后备字段,并用公共名称暴露 getter 或 setter。
import { column } from '@adonisjs/lucid/orm'
import { UsersSchema } from '#database/schema'
export default class User extends UsersSchema {
@column({ columnName: 'avatar_path' })
declare avatarPath: string | null
get avatarUrl(): string | null {
return this.avatarPath
? `https://cdn.example.com/${this.avatarPath}`
: null
}
}
对于仍需落入列中的写端转换,prepare 通常是更好的选择,因为它在每次插入和更新时自动运行。当转换依赖于赋值时的其他模型属性时才使用 setter。
类型兼容性约束
TypeScript 强制要求重新声明的属性类型与基类的类型兼容。你可以缩窄类型,但不能将其更改为不兼容的类型。
// 有效:将 string 缩窄为联合类型
@column()
declare status: 'draft' | 'published'
// 有效:将 any 缩窄为特定类型
@column()
declare metadata: JSON<{ title: string }>
// 无效:不能将 string 更改为 number
@column()
declare title: number
// 无效:不能将 number 更改为 string
@column()
declare id: string
当你需要从根本上更改列的类型时,使用 schema 规则在生成时设置正确的类型;然后模型可以进一步细化生成的类型。参见组合全局和表规则。