mcpField
Z
zenstack-schema-modeling

zenstack-schema-modeling

github.com/zenstackhq数据与数据库3437 次安装5 天前更新
代码仓库

关于 zenstack-schema-modeling

zenstack-schema-modeling 是介绍 ZenStack ZModel 的建模技能,面向定义数据库结构的开发者。ZModel 扩展 Prisma Schema,并提供混入、多态模型、强类型 JSON 和计算字段等建模方式。

主要功能

  1. 01模型、字段与枚举定义
  2. 02一对一及多对多关系
  3. 03自定义类型与字段混入
  4. 04多态模型继承
  5. 05强类型 JSON 字段
  6. 06计算字段与模式校验

适用场景

  1. 01为应用定义数据库模型和关联
  2. 02在多个模型间复用字段
  3. 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/ 下:

评论

支持 Markdown