跳至主要内容

原型设计你的 Schema

Prisma CLI 有一个专门用于原型设计 Schema 的命令:db push

db push 使用与 Prisma Migrate 相同的引擎来同步您的 Prisma Schema 和您的数据库 Schema。db push 命令

  1. 内省数据库以推断并执行使数据库 Schema 反映 Prisma Schema 状态所需的更改。

  2. 默认情况下,在将更改应用于数据库 Schema 后,会触发生成器(例如,Prisma Client)。您无需手动调用 prisma generate

  3. 如果 db push 预计更改可能导致数据丢失,它将

    • 抛出一个错误
    • 如果您仍然想要进行更改,则需要 --accept-data-loss 选项

注意:

  • db push 不与迁移交互或依赖于迁移。迁移表 _prisma_migrations 不会被创建或更新,并且不会生成任何迁移文件。
  • 在使用 PlanetScale 时,我们建议您使用 db push 而不是 migrate。有关详细信息,请参阅我们的入门文档,具体取决于您的情况,请参阅从头开始添加到现有项目

选择 db push 或 Prisma Migrate

如果满足以下条件,db push 效果很好:

  • 您希望在本地快速原型设计和迭代 Schema 设计,而无需将这些更改部署到其他环境(例如其他开发人员或暂存和生产环境)。
  • 您优先考虑达到所需的最终状态,而不是达到该最终状态所执行的更改或步骤(无法预览 db push 所做的更改)。
  • 您不需要控制 Schema 更改如何影响数据。无法编排 Schema 和数据迁移 - 如果 db push 预计更改会导致数据丢失,您可以使用 --accept-data-loss 选项接受数据丢失或停止该过程。无法自定义更改。

请参阅使用 db push 进行 Schema 原型设计,了解如何以这种方式使用 db push 的示例。

如果满足以下条件,则不建议使用 db push

  • 您希望在其他环境中复制您的 Schema 更改,而不会丢失数据。您可以使用 db push 进行原型设计,但应使用迁移来提交 Schema 更改并在其他环境中应用这些更改。
  • 您希望对 Schema 更改的执行方式进行细粒度控制 - 例如,重命名列而不是删除它并创建一个新的列
  • 您希望跟踪对数据库 Schema 所做的更改。db push 不会创建任何允许您跟踪这些更改的工件。
  • 您希望 Schema 更改可逆。您可以再次使用 db push 回到原始状态,但这可能会导致数据丢失。

我可以一起使用 Prisma Migrate 和 db push 吗?

是的,您可以在开发工作流程中一起使用 db push 和 Prisma Migrate。例如,您可以

  • 使用 db push 在项目开始时对 Schema 进行原型设计,并在对第一个草稿满意时初始化迁移历史记录。
  • 使用 db push 对现有 Schema 的更改进行原型设计,然后运行 prisma migrate dev 从更改中生成迁移(系统会提示您重置)。

原型设计新的 Schema

以下场景演示了如何使用 db push 将新的 Schema 与空数据库同步,以及如何演化该 Schema - 包括 db push 检测到更改会导致数据丢失时会发生什么。

  1. 创建 Schema 的第一个草稿。

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

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

    model User {
    id Int @id @default(autoincrement())
    name String
    jobTitle String
    posts Post[]
    profile Profile?
    }

    model Profile {
    id Int @id @default(autoincrement())
    biograpy String // Intentional typo!
    userId Int @unique
    user User @relation(fields: [userId], references: [id])
    }

    model Post {
    id Int @id @default(autoincrement())
    title String
    published Boolean @default(true)
    content String @db.VarChar(500)
    authorId Int
    author User @relation(fields: [authorId], references: [id])
    categories Category[]
    }

    model Category {
    id Int @id @default(autoincrement())
    name String @db.VarChar(50)
    posts Post[]

    @@unique([name])
    }
  2. 使用 db push 将初始 Schema 推送到数据库。

    npx prisma db push
  3. 创建一些示例内容。

    const add = await prisma.user.create({
    data: {
    name: 'Eloise',
    jobTitle: 'Programmer',
    posts: {
    create: {
    title: 'How to create a MySQL database',
    content: 'Some content',
    },
    },
    },
    })
  4. 进行增量更改 - 例如,创建一个新的必填字段。

    // ... //

    model Post {
    id Int @id @default(autoincrement())
    title String
    description String
    published Boolean @default(true)
    content String @db.VarChar(500)
    authorId Int
    author User @relation(fields: [authorId], references: [id])
    categories Category[]
    }

    // ... //
  5. 推送更改。

    npx prisma db push

    db push 将提示您重置,因为除非您提供默认值,否则无法将必填字段添加到包含现有内容的表中。

    ⚠️ We found changes that cannot be executed:

    • Added the required column `description` to the `Post` table without a default value. There are 2 rows in this table, it is not possible to execute this.

    ? To apply this step we need to reset the database, do you want to continue? All data will be lost. » (y/N)
提示

使用 --accept-data-loss 标志跳过此警告,或使用 --force-reset 忽略所有警告。

  1. 确认数据丢失并将更改应用于您的数据库(或重新审视您的 Schema)。

     There might be data loss when applying the changes:

    • Added the required column `description` to the `Post` table without a default value.

    ? Do you want to ignore the warning(s)? Some data will be lost. » (y/N)

    注意:与 Prisma Migrate 不同,db push 不会生成您可以修改以保留数据的迁移,因此最适合在开发环境中进行原型设计。

  2. 继续演化您的 Schema,直到它达到相对稳定的状态。

  3. 初始化迁移历史记录。

    npx prisma migrate dev --name initial-state

    为达到初始原型而采取的步骤不会保留 - db push 不会生成历史记录。

  4. 将您的迁移历史记录和 Prisma Schema 推送到源代码控制(例如 Git)。

此时,您的原型设计的最终草稿将保存在迁移中,并且可以推送到其他环境(测试、生产或您的团队的其他成员)。

在现有迁移历史记录中进行原型设计

以下场景演示了如何使用 db push 对已经存在迁移历史记录的 Prisma Schema 进行更改的原型设计。

  1. 检出最新的 Prisma Schema 和迁移历史记录。

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

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

    model User {
    id Int @id @default(autoincrement())
    name String
    jobTitle String
    posts Post[]
    profile Profile?
    }

    model Profile {
    id Int @id @default(autoincrement())
    biograpy String // Intentional typo!
    userId Int @unique
    user User @relation(fields: [userId], references: [id])
    }

    model Post {
    id Int @id @default(autoincrement())
    title String
    published Boolean @default(true)
    content String @db.VarChar(500)
    authorId Int
    author User @relation(fields: [authorId], references: [id])
    categories Category[]
    }

    model Category {
    id Int @id @default(autoincrement())
    name String @db.VarChar(50)
    posts Post[]

    @@unique([name])
    }
  2. 对您的新功能进行原型设计,这可能涉及任意数量的步骤。例如,您可能会

    • 创建一个 tags String[] 字段,然后运行 db push
    • 将字段类型更改为 tags Tag[] 并添加一个名为 Tag 的新模型,然后运行 db push
    • 改变主意并恢复原始的 tags String[] 字段,然后调用 db push
    • 手动更改数据库中的 tags 字段 - 例如,添加约束。

    在尝试了几种解决方案后,最终的 Schema 更改如下所示。

    model Post {
    id Int @id @default(autoincrement())
    title String
    description String
    published Boolean @default(true)
    content String @db.VarChar(500)
    authorId Int
    author User @relation(fields: [authorId], references: [id])
    categories Category[]
    tags String[]
    }
  3. 要创建添加新 tags 字段的迁移,请运行 migrate dev 命令。

    npx prisma migrate dev --name added-tags

    Prisma Migrate 将提示您重置,因为您在原型设计期间手动和使用 db push 所做的更改不属于迁移历史记录。

    √ Drift detected: Your database schema is not in sync with your migration history.

    We need to reset the PostgreSQL database "prototyping" at "localhost:5432".
    Do you want to continue? All data will be lost. ... yes
  4. Prisma Migrate 重放现有的迁移历史记录,根据您的 Schema 更改生成新的迁移,并将这些更改应用于数据库。

提示

在使用 migrate dev 时,如果您的 Schema 更改意味着种子脚本将不再起作用,您可以使用 --skip-seed 标志忽略种子脚本。

此时,您的原型设计的最终结果将保存在迁移中,并且可以推送到其他环境(测试、生产或您的团队的其他成员)。