跳到主要内容

模型

Prisma schema 的数据模型定义部分定义了你的应用程序模型(也称为 Prisma 模型)。模型

  • 代表你的应用程序域的实体
  • 映射到数据库中的(关系型数据库如 PostgreSQL)或集合(MongoDB)
  • 构成生成的 Prisma Client API 中可用查询的基础
  • 与 TypeScript 一起使用时,Prisma Client 为你的模型及其任何变体提供生成的类型定义,以使数据库访问完全类型安全。

以下模式描述了一个博客平台 - 数据模型定义被高亮显示

datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}

generator client {
provider = "prisma-client-js"
}

model User {
id Int @id @default(autoincrement())
email String @unique
name String?
role Role @default(USER)
posts Post[]
profile Profile?
}

model Profile {
id Int @id @default(autoincrement())
bio String
user User @relation(fields: [userId], references: [id])
userId Int @unique
}

model Post {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
title String
published Boolean @default(false)
author User @relation(fields: [authorId], references: [id])
authorId Int
categories Category[]
}

model Category {
id Int @id @default(autoincrement())
name String
posts Post[]
}

enum Role {
USER
ADMIN
}

数据模型定义由以下部分组成

对应的数据库看起来像这样

Sample database

模型映射到数据源的底层结构。
  • 在 PostgreSQL 和 MySQL 等关系型数据库中,model 映射到一张
  • 在 MongoDB 中,model 映射到一个集合

注意:未来可能会有针对非关系型数据库和其他数据源的连接器。例如,对于 REST API,它将映射到资源

以下查询使用从这个数据模型生成的 Prisma Client 来创建

  • 一个 User 记录
  • 两个嵌套的 Post 记录
  • 三个嵌套的 Category 记录
const user = await prisma.user.create({
data: {
email: 'ariadne@prisma.io',
name: 'Ariadne',
posts: {
create: [
{
title: 'My first day at Prisma',
categories: {
create: {
name: 'Office',
},
},
},
{
title: 'How to connect to a SQLite database',
categories: {
create: [{ name: 'Databases' }, { name: 'Tutorials' }],
},
},
],
},
},
})

你的数据模型反映了你的应用程序域。例如

  • 电子商务应用程序中,你可能拥有 CustomerOrderItemInvoice 等模型。
  • 社交媒体应用程序中,你可能拥有 UserPostPhotoMessage 等模型。

内省和迁移

定义数据模型有两种方式

  • 手动编写数据模型并使用 Prisma Migrate:你可以手动编写数据模型并使用 Prisma Migrate 将其映射到你的数据库。在这种情况下,数据模型是应用程序模型的单一真实来源。
  • 通过内省生成数据模型:当你拥有现有数据库或偏好使用 SQL 迁移数据库模式时,你可以通过内省数据库来生成数据模型。在这种情况下,数据库模式是应用程序模型的单一真实来源。

定义模型

模型代表你的应用程序域的实体。模型由 model 块表示,并定义了许多字段。在上面的示例数据模型中,UserProfilePostCategory 都是模型。

博客平台可以通过以下模型进行扩展

model Comment {
// Fields
}

model Tag {
// Fields
}

将模型名称映射到表或集合

Prisma 模型的命名约定(单数形式,PascalCase)并不总是与数据库中的表名匹配。数据库中命名表/集合的常见方法是使用复数形式和snake_case表示法——例如:comments。当你内省一个名为 comments 的数据库表时,结果 Prisma 模型将如下所示

model comments {
// Fields
}

但是,你仍然可以通过使用 @@map 属性来遵守命名约定,而无需重命名数据库中底层的 comments

model Comment {
// Fields

@@map("comments")
}

通过此模型定义,Prisma ORM 会自动将 Comment 模型映射到底层数据库中的 comments 表。

注意:你也可以 @map 一个列名或枚举值,以及 @@map 一个枚举名。

@map@@map 允许你通过将模型和字段名称与底层数据库中的表和列名称解耦,从而调整 Prisma Client API 的形状

定义字段

模型的属性称为字段,它们包括

字段的类型决定其结构,并分为两类

  • 标量类型(包括枚举),它们映射到数据库中的列(关系型数据库)或文档字段(MongoDB)——例如,StringInt
  • 模型类型(此时该字段称为关系字段)——例如 PostComment[]

下表描述了示例模式中 User 模型的字段

展开以查看表格
名称类型标量 vs 关系类型修饰符属性
idInt标量-@id@default(autoincrement())
emailString标量-@unique
nameString标量?-
roleRole标量 (enum)-@default(USER)
postsPost关系 (Prisma 级别字段)[]-
profileProfile关系 (Prisma 级别字段)?-

标量字段

以下示例使用多个标量类型扩展了 CommentTag 模型。有些字段包含属性

model Comment {
id Int @id @default(autoincrement())
title String
content String
}

model Tag {
name String @id
}

参阅标量字段类型的完整列表

关系字段

关系字段的类型是另一个模型——例如,一个帖子 (Post) 可以有多个评论 (Comment[])

model Post {
id Int @id @default(autoincrement())
// Other fields
comments Comment[] // A post can have many comments
}

model Comment {
id Int
// Other fields
post Post? @relation(fields: [postId], references: [id]) // A comment can have one post
postId Int?
}

有关模型之间关系的更多示例和信息,请参阅关系文档

原生类型映射

版本 2.17.0 及更高版本支持原生数据库类型属性(类型属性),它们描述了底层数据库类型

model Post {
id Int @id
title String @db.VarChar(200)
content String
}

类型属性是

  • 特定于底层提供者——例如,PostgreSQL 对 Boolean 使用 @db.Boolean,而 MySQL 使用 @db.TinyInt(1)
  • 以 PascalCase 编写(例如,VarCharText
  • @db 为前缀,其中 db 是模式中 datasource 块的名称

此外,在内省期间,只有当底层原生类型**不是默认类型**时,类型属性才会被添加到模式中。例如,如果你使用的是 PostgreSQL 提供程序,那么底层原生类型为 textString 字段将不会有类型属性。

请参阅每种标量类型和提供者的原生数据库类型属性的完整列表

优势和工作流

  • 控制 Prisma Migrate 在数据库中创建的精确原生类型——例如,一个 String 可以是 @db.VarChar(200)@db.Char(50)
  • 当你内省时,查看丰富的模式

类型修饰符

字段类型可以通过附加以下两种修饰符之一进行修改

  • [] 将字段设为列表
  • ? 将字段设为可选

注意:你**不能**组合类型修饰符——不支持可选列表。

列表

以下示例包含标量列表和相关模型列表

model Post {
id Int @id @default(autoincrement())
// Other fields
comments Comment[] // A list of comments
keywords String[] // A scalar list
}

注意:只有当数据库连接器原生或在 Prisma ORM 级别支持标量列表时,才支持标量列表。

可选字段和必填字段

model Comment {
id Int @id @default(autoincrement())
title String
content String?
}

model Tag {
name String @id
}

当一个字段**未**用 ? 类型修饰符注释时,该字段在模型的每条记录上都是必需的。这在两个层面产生影响

  • 数据库
    • 关系型数据库:必需字段通过底层数据库中的 NOT NULL 约束表示。
    • MongoDB:必需字段在 MongoDB 数据库级别不是一个概念。
  • Prisma Client:Prisma Client 生成的代表应用程序代码中模型的TypeScript 类型也将这些字段定义为必需,以确保它们在运行时始终带有值。

注意:可选字段的默认值为 null

不支持的类型

当你内省关系型数据库时,不支持的数据类型会以 Unsupported 的形式添加

location    Unsupported("POLYGON")?

Unsupported 类型允许你在 Prisma 模式中定义 Prisma ORM 尚未支持的数据库类型的字段。例如,MySQL 的 POLYGON 类型目前不受 Prisma ORM 支持,但现在可以使用 Unsupported("POLYGON") 类型添加到 Prisma 模式中。

类型为 Unsupported 的字段不会出现在生成的 Prisma Client API 中,但你仍然可以使用 Prisma ORM 的原始数据库访问功能来查询这些字段。

注意:如果模型有**强制性的 Unsupported 字段**,则生成的客户端将不包含该模型的 createupdate 方法。

注意:MongoDB 连接器不支持也不要求 Unsupported 类型,因为它支持所有标量类型。

定义属性

属性修改字段或模型块的行为。以下示例包含三个字段属性(@id@default@unique)和一个块属性(@@unique

model User {
id Int @id @default(autoincrement())
firstName String
lastName String
email String @unique
isAdmin Boolean @default(false)

@@unique([firstName, lastName])
}

一些属性接受参数——例如,@default 接受 truefalse

isAdmin   Boolean @default(false) // short form of @default(value: false)

请参阅字段和块属性的完整列表

定义 ID 字段

ID 唯一标识模型的单个记录。一个模型只能有一个 ID

  • 在**关系型数据库**中,ID 可以是单个字段或基于多个字段。如果模型没有 @id@@id,则必须定义一个强制性的 @unique 字段或 @@unique 块来代替。
  • 在**MongoDB**中,ID 必须是一个定义了 @id 属性和 @map("_id") 属性的单一字段。

在关系型数据库中定义 ID

在关系型数据库中,ID 可以通过使用 @id 属性的单个字段来定义,或者通过使用 @@id 属性的多个字段来定义。

单字段 ID

在以下示例中,User 的 ID 由 id 整数字段表示

model User {
id Int @id @default(autoincrement())
email String @unique
name String?
role Role @default(USER)
posts Post[]
profile Profile?
}
复合 ID

在下面的例子中,User 的 ID 由 firstNamelastName 字段的组合表示

model User {
firstName String
lastName String
email String @unique
isAdmin Boolean @default(false)

@@id([firstName, lastName])
}

默认情况下,此字段在 Prisma Client 查询中的名称将为 firstName_lastName

你也可以使用 @@id 属性的 name 字段为复合 ID 提供自己的名称

model User {
firstName String
lastName String
email String @unique
isAdmin Boolean @default(false)

@@id(name: "fullName", fields: [firstName, lastName])
}

firstName_lastName 字段现在将命名为 fullName

信息

请参阅使用复合 ID 的文档,了解如何在 Prisma Client 中与复合 ID 交互。

@unique 字段作为唯一标识符

在以下示例中,用户通过 @unique 字段唯一标识。由于 email 字段充当模型的唯一标识符(这是必需的),因此它必须是强制性的

model User {
email String @unique
name String?
role Role @default(USER)
posts Post[]
profile Profile?
}
信息

关系型数据库中的约束名称
你可以选择在底层数据库中定义一个自定义主键约束名称

在 MongoDB 中定义 ID

MongoDB 连接器对于定义 ID 字段有特定的规则,这些规则与关系型数据库不同。ID 必须由一个使用 @id 属性的单个字段定义,并且必须包含 @map("_id")

在以下示例中,User 的 ID 由 id 字符串字段表示,该字段接受一个自动生成的 ObjectId

model User {
id String @id @default(auto()) @map("_id") @db.ObjectId
email String @unique
name String?
role Role @default(USER)
posts Post[]
profile Profile?
}

在以下示例中,User 的 ID 由 id 字符串字段表示,该字段接受除 ObjectId 之外的值——例如,一个唯一的用户名

model User {
id String @id @map("_id")
email String @unique
name String?
role Role @default(USER)
posts Post[]
profile Profile?
}
警告

MongoDB 不支持 @@id
MongoDB 不支持复合 ID,这意味着你不能用 @@id 块来标识一个模型。

定义默认值

你可以使用 @default 属性为模型的标量字段定义默认值

model Post {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
title String
published Boolean @default(false)
data Json @default("{ \"hello\": \"world\" }")
author User @relation(fields: [authorId], references: [id])
authorId Int
categories Category[] @relation(references: [id])
}

@default 属性要么

  • 代表底层数据库中的 DEFAULT 值(仅限关系型数据库)或者
  • 使用 Prisma ORM 级别的函数。例如,cuid()uuid() 由 Prisma Client 的查询引擎为所有连接器提供。

默认值可以是

  • 与字段类型对应的静态值,例如 5 (Int)、Hello (String) 或 false (Boolean)
  • 静态值的列表,例如 [5, 6, 8] (Int[]) 或 ["Hello", "Goodbye"] (String[])。这些在 Prisma ORM 4.0.0 及更高版本中可用,并且在使用支持的数据库(PostgreSQL、CockroachDB 和 MongoDB)时。
  • 函数,例如 now()uuid()
  • JSON 数据。请注意,JSON 需要在 @default 属性中用双引号括起来,例如:@default("[]")。如果要提供 JSON 对象,需要用双引号括起来,然后使用反斜杠转义任何内部双引号,例如:@default("{ \"hello\": \"world\" }")
信息

有关连接器对函数支持的信息,请参阅属性函数参考文档

定义唯一字段

你可以向模型添加唯一属性,以便能够唯一标识该模型的单个记录。唯一属性可以在单个字段上使用@unique属性定义,也可以在多个字段上(也称为复合或组合唯一约束)使用@@unique属性定义。

在以下示例中,email 字段的值必须是唯一的

model User {
id Int @id @default(autoincrement())
email String @unique
name String?
}

在以下示例中,authorIdtitle 的组合必须是唯一的

model Post {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
title String
published Boolean @default(false)
author User @relation(fields: [authorId], references: [id])
authorId Int
categories Category[] @relation(references: [id])

@@unique([authorId, title])
}
信息

关系型数据库中的约束名称
你可以选择在底层数据库中定义一个自定义唯一约束名称

默认情况下,此字段在 Prisma Client 查询中的名称将为 authorId_title

你也可以使用 @@unique 属性的 name 字段为复合唯一约束提供自己的名称

model Post {
id String @id @default(auto()) @map("_id") @db.ObjectId
createdAt DateTime @default(now())
title String
published Boolean @default(false)
author User @relation(fields: [authorId], references: [id])
authorId String @db.ObjectId
categories Category[] @relation(references: [id])

@@unique(name: "authorTitle", [authorId, title])
}

authorId_title 字段现在将命名为 authorTitle

信息

有关使用复合唯一标识符的文档,请参阅如何在 Prisma Client 中与复合唯一约束交互。

复合类型唯一约束

在使用版本 3.12.0 及更高版本的 MongoDB 提供程序时,你可以使用语法 @@unique([compositeType.field])复合类型的字段上定义唯一约束。与其他字段一样,复合类型字段可以用作多列唯一约束的一部分。

以下示例定义了一个基于 User 模型的 email 字段和 Address 复合类型(在 User.address 中使用)的 number 字段的多列唯一约束

schema.prisma
type Address {
street String
number Int
}

model User {
id Int @id
email String
address Address

@@unique([email, address.number])
}

如果存在多个嵌套复合类型,则可以链式使用此表示法

schema.prisma
type City {
name String
}

type Address {
number Int
city City
}

model User {
id Int @id
address Address[]

@@unique([address.city.name])
}

定义索引

你可以通过模型上的 @@index 在模型的一个或多个字段上定义索引。以下示例定义了一个基于 titlecontent 字段的多列索引

model Post {
id Int @id @default(autoincrement())
title String
content String?

@@index([title, content])
}
信息

关系型数据库中的索引名称
你可以选择在底层数据库中定义一个自定义索引名称

定义复合类型索引

当在版本 3.12.0 及更高版本中使用 MongoDB 提供程序时,你可以使用语法 @@index([compositeType.field])复合类型的字段上定义索引。与其他字段一样,复合类型字段可以用作多列索引的一部分。

以下示例定义了一个基于 User 模型的 email 字段和 Address 复合类型的 number 字段的多列索引

schema.prisma
type Address {
street String
number Int
}

model User {
id Int @id
email String
address Address

@@index([email, address.number])
}

如果存在多个嵌套复合类型,则可以链式使用此表示法

schema.prisma
type City {
name String
}

type Address {
number Int
city City
}

model User {
id Int @id
address Address[]

@@index([address.city.name])
}

定义枚举

如果你的数据库连接器支持枚举(原生或在 Prisma ORM 级别),你可以在数据模型中定义枚举。

枚举在 Prisma schema 数据模型中被视为标量类型。因此,它们默认情况下作为返回值包含在Prisma Client 查询中。

枚举通过 enum 块定义。例如,User 具有 Role

model User {
id Int @id @default(autoincrement())
email String @unique
name String?
role Role @default(USER)
}

enum Role {
USER
ADMIN
}

定义复合类型

信息

复合类型是在版本 3.10.0 中以 mongodb 预览功能标志添加的,并自版本 3.12.0 起普遍可用。

警告

复合类型目前仅在 MongoDB 上可用。

复合类型(在 MongoDB 中称为嵌入式文档)通过允许你定义新的对象类型来支持在其他记录中嵌入记录。复合类型的结构和类型与模型类似。

要定义复合类型,请使用 type 块。例如,请看以下模式

schema.prisma
model Product {
id String @id @default(auto()) @map("_id") @db.ObjectId
name String
photos Photo[]
}

type Photo {
height Int
width Int
url String
}

在这种情况下,Product 模型有一个存储在 photos 中的 Photo 复合类型列表。

使用复合类型时的注意事项

复合类型仅支持有限的属性集。支持以下属性

复合类型中不支持以下属性

  • @unique
  • @id
  • @relation
  • @ignore
  • @updatedAt

然而,唯一约束仍然可以通过在复合类型所使用的模型级别上使用 @@unique 属性来定义。更多详情,请参阅复合类型唯一约束

索引可以通过在复合类型所使用的模型级别上使用 @@index 属性来定义。更多详情,请参阅复合类型索引

使用函数

Prisma schema 支持许多函数。这些函数可用于指定模型字段的默认值

例如,createdAt 的默认值是 now()

model Post {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
}

cuid()uuid() 由 Prisma ORM 实现,因此在底层数据库模式中是“不可见”的。你仍然可以通过手动更改你的 Prisma schema生成 Prisma Client 来在使用内省时使用它们,在这种情况下,这些值将由 Prisma Client 的查询引擎生成

autoincrement()now()dbgenerated(...) 的支持因数据库而异。

关系型数据库连接器在数据库层面实现 autoincrement()dbgenerated(...)now()MongoDB 连接器不支持 autoincrement()dbgenerated(...),并且 now() 在 Prisma ORM 层面实现。auto() 函数用于生成 ObjectId

关系

有关模型之间关系的更多示例和信息,请参阅关系文档

Prisma Client 中的模型

查询 (CRUD)

数据模型定义中的每个模型都将在生成的Prisma Client API中产生许多 CRUD 查询

这些操作可通过 Prisma Client 实例上的生成属性进行访问。默认情况下,该属性的名称是模型名称的小写形式,例如,User 模型为 userPost 模型为 post

以下是说明 Prisma Client API 中 user 属性使用情况的示例

const newUser = await prisma.user.create({
data: {
name: 'Alice',
},
})
const allUsers = await prisma.user.findMany()

类型定义

Prisma Client 还生成反映你的模型结构的**类型定义**。这些是生成的@prisma/client Node 模块的一部分。

使用 TypeScript 时,这些类型定义确保你的所有数据库查询在编译时完全类型安全并经过验证(即使是使用 selectinclude 的部分查询)。

即使使用纯 JavaScript,类型定义也仍然包含在 @prisma/client Node 模块中,从而在你的编辑器中启用 IntelliSense/自动完成等功能。

注意:实际类型存储在 .prisma/client 文件夹中。@prisma/client/index.d.ts 导出此文件夹的内容。

例如,上面 User 模型的类型定义将如下所示

export type User = {
id: number
email: string
name: string | null
role: string
}

请注意,关系字段 postsprofile 默认不包含在类型定义中。但是,如果你需要 User 类型的变体,你仍然可以使用 Prisma Client 生成的一些辅助类型来定义它们(在这种情况下,这些辅助类型将被称为 UserGetIncludePayloadUserGetSelectPayload)。

限制

记录必须是唯一可识别的

Prisma ORM 目前仅支持至少有一个唯一字段或字段组合的模型。实际上,这意味着每个 Prisma 模型都必须具有以下至少一个属性

  • @id@@id 用于单字段或多字段主键约束(每个模型最多一个)
  • @unique@@unique 用于单字段或多字段唯一约束
© . All rights reserved.