关于 zenstack-schema-modeling
zenstack-schema-modeling 是介绍 ZenStack ZModel 的建模技能,面向定义数据库结构的开发者。ZModel 扩展 Prisma Schema,并提供混入、多态模型、强类型 JSON 和计算字段等建模方式。
主要功能
- 01模型、字段与枚举定义
- 02一对一及多对多关系
- 03自定义类型与字段混入
- 04多态模型继承
- 05强类型 JSON 字段
- 06计算字段与模式校验
适用场景
- 01为应用定义数据库模型和关联
- 02在多个模型间复用字段
- 03为多态数据或 JSON 数据建模
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ zenstackhq/ skills --skill zenstack-schema-modeling安装后在 Claude Code 中输入/zenstack-schema-modeling确认已加载。
详细介绍
ZenStack — 模式建模(ZModel)
ZModel 是 ZenStack 的模式语言,是 Prisma 模式的超集。模式默认位于
zenstack/schema.zmodel。访问策略和验证请使用 zenstack-access-control;
查询请使用 zenstack-querying。
编辑模式后:
zen check # only validate the schema for syntax/semantic errors (use --schema <file> for a non-default path)
zen generate # regenerate the database client — also validates the schema as it compiles
只想确认模式是否有效时,运行 zen check;需要重新生成客户端时,运行 zen generate
(模式变更后的常规步骤)。
字符串既可以使用单引号,也可以使用双引号。每个有效的 Prisma 模式都是有效的 ZModel,因此现有的 Prisma 知识可以直接迁移使用——以下各节介绍基础内容,以及 ZModel 在此基础上扩展的内容。
数据源
每个模式中必须且只能有一个 datasource 块。ORM 运行时不使用 url(连接由驱动程序负责);迁移引擎会使用它。
datasource db {
provider = 'postgresql' // 'postgresql' | 'mysql' | 'sqlite'
url = env('DATABASE_URL') // or 'file:./dev.db' for SQLite
}
ZModel 没有
generator块。代码生成由zen generate驱动;可选行为通过plugin块配置(例如plugin policy、plugin prisma)。
模型和字段
model User {
id Int @id @default(autoincrement())
email String @unique
name String? // optional
role Role @default(USER)
tags String[] // list
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
标量类型:String、Boolean、Int、BigInt、Float、Decimal、DateTime、Json、
Bytes、Unsupported("...")。
修饰符:? 表示可选,[] 表示列表。字段不能同时为可选字段和列表。
字段属性(@)
@id、@unique、@default(...)、@updatedAt、@map('col')、@relation(...)、@db.*(原生类型,例如 @db.VarChar(64))、@json(类型化 JSON 字段)、@computed(虚拟字段)。
模型属性(@@)
@@id([a, b])(复合主键)、@@unique([a, b])、@@index([a, b])、@@map('table')、
@@delegate(field)(多态),以及 @@allow / @@deny / @@validate(参见
zenstack-access-control)。
model City {
country String
name String
@@id([country, name]) // composite primary key
@@map('cities')
}
模型需要标识符:@id 字段、@@id([...]),或者在前两者均未使用时,@unique 字段或 @@unique([...])。
默认值函数
autoincrement()、now()、cuid()、uuid() / uuid(4)、ulid()、nanoid()、
dbgenerated("..."),以及字面值和枚举值。
从 v3.1.0 起,字符串 ID 生成器支持使用包含 %s 的 format:
id String @id @default(uuid(4, "user_%s")) // -> "user_<uuid>"
枚举
enum Role {
USER
ADMIN
}
枚举值名称是全局的,不得重复。
关系
所有者一侧保存外键(@relation(fields: [...], references: [...]))。
一对一——外键为 @unique;非所有者一侧为可选:
model User {
id Int @id
profile Profile?
}
model Profile {
id Int @id
user User @relation(fields: [userId], references: [id])
userId Int @unique
}
一对多——非所有者一侧使用列表:
model User {
id Int @id
posts Post[]
}
model Post {
id Int @id
author User @relation(fields: [authorId], references: [id])
authorId Int
}
多对多(隐式)——两侧都使用列表;迁移引擎会创建联结表:
model User { id Int @id posts Post[] }
model Post { id Int @id editors User[] }
多对多(显式)——当联结表需要额外字段时,手动建模:
model UserPost {
user User @relation(fields: [userId], references: [id])
userId Int
post Post @relation(fields: [postId], references: [id])
postId Int
@@id([userId, postId])
}
命名关系用于区分同一对模型之间的多个关系(两端使用相同名称):@relation('UserPosts', ...)。
自关联使用命名关系,例如一对多的经理/下属树:
model Employee {
id Int @id
managerId Int?
manager Employee? @relation('Mgmt', fields: [managerId], references: [id])
subordinates Employee[] @relation('Mgmt')
}
引用操作在所有者一侧设置:onDelete / onUpdate 可使用 Cascade、
Restrict、NoAction、SetNull、SetDefault。
author User @relation(fields: [authorId], references: [id], onDelete: Cascade)
自定义类型和混入
type 声明不会对应数据库表。可将其用于类型化 JSON(见下文),或作为可复用字段的混入,通过 with 应用:
type BaseFields {
id String @id @default(cuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model User with BaseFields {
email String @unique
}
model Post with BaseFields {
title String
}
混入字段会内联到模型中。只要名称不冲突,就可以使用多个混入。这是 ZModel 在多个模型之间共享字段的机制(应使用它,而不是抽象基类模型)。
类型甚至可以包含关系字段(v3.6.0+),但此时只能将其用作混入,不能用作 JSON 字段:
type AuditMixin {
createdBy User @relation('CreatedBy', fields: [createdById], references: [id])
createdById String
}
model Post with AuditMixin { title String }
多态——@@delegate
多表继承:基类模型包含一个判别字段和 @@delegate,具体模型使用 extends。具体模型和基类的行共享相同的 ID;创建的是具体模型,而不是直接创建基类。
model Content {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
owner User @relation(fields: [ownerId], references: [id])
ownerId Int
type String // discriminator
@@delegate(type)
}
model Post extends Content {
content String
}
model Image extends Content {
data Bytes
}
使用 @@delegateMap(v3.7.1+)自定义存储的判别值;它也支持使用枚举作为判别器:
model Video extends Content {
url String
@@delegateMap("video")
}
强类型 JSON
定义一个 type,将其用作字段类型,并为字段标记 @json。数据库中的列是普通 JSON,但 ZenStack 会验证写入的数据,并返回类型化的值。
type Address {
street String
city String
zip Int
}
model User {
id Int @id
address Address @json
}
计算字段
使用 @computed 声明虚拟字段;在客户端配置中使用 Kysely 表达式实现该字段。可以选择、筛选、排序该字段,也可以在策略中引用它。
model User {
id Int @id
postCount Int @computed
}
const db = new ZenStackClient(schema, {
dialect,
computedFields: {
User: {
postCount: (eb) =>
eb.selectFrom('Post')
.whereRef('Post.authorId', '=', 'id')
.select(({ fn }) => fn.countAll<number>().as('count')),
},
},
});
相较于 Prisma 的 ZModel 扩展
- 没有
generator块(使用zen generate和plugin)。 type声明与with混入、@@delegate多态、@json类型化 JSON 以及@computed字段都是 ZenStack 新增的功能,Prisma 不具备。- 单引号和双引号均可使用。
参考文档
此主题的完整 ZenStack 文档已包含在 references/ 下:
- modeling-overview.md — 数据建模概念
- datasource.md —
datasource块 - model.md — 模型、字段、ID、默认值
- attribute.md — 字段和模型属性
- relation.md — 一对一、一对多、多对多、自关联
- enum.md — 枚举
- custom-type.md —
type声明 - mixin.md — 可复用字段混入(
with) - polymorphism.md —
@@delegate多态模型 - typed-json.md — 强类型 JSON 字段
- computed-fields.md —
@computed字段 - zmodel-attribute-reference.md — 完整属性参考
- zmodel-function-reference.md — 属性函数参考