跳到主要内容

模型

Prisma 架构 定义了您的应用程序模型(也称为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: '[email protected]',
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) 并不总是与数据库中的表名称相匹配。在数据库中命名表/集合的常用方法是使用复数形式和 蛇形命名法 符号 - 例如: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 使用 @db.Boolean 表示 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 ORM 尚未支持的数据库类型在 Prisma 模式中定义字段。例如,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

info

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

@unique 字段作为唯一标识符

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

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

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

在 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?
}
warning

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\" }")
info

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

定义唯一字段

你可以为模型添加唯一属性,以便能够唯一标识该模型的单个记录。唯一属性可以在单个字段上使用 @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])
}
info

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

默认情况下,此字段在 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

info

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

复合类型唯一约束

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

以下示例定义了一个基于 User 模型的 email 字段和 User.address 中使用的 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])
}
info

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

定义复合类型索引

在使用 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 模式数据模型中被视为 标量 类型。因此,它们 默认情况下 被包含在 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
}

定义复合类型

info

复合类型在 3.10.0 版本中作为 mongodb 预览功能标志添加,并在 3.12.0 版本中全面发布。

warning

复合类型目前仅在 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 模式支持许多 函数。这些函数可用于指定模型字段的 默认值

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

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

cuid()uuid() 由 Prisma ORM 实现,因此在底层数据库模式中不可见。您仍然可以在使用 内省 时使用它们,方法是 手动更改 Prisma 模式生成 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 节点模块的一部分。

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

即使使用纯 JavaScript,类型定义也会包含在 @prisma/client 节点模块中,从而启用编辑器中的 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 用于单字段或多字段唯一约束。