使用 prisma 进行播种,以一对一关系嵌套创建

问题描述 投票:0回答:2

目前正在开发一个项目,以通过 Nestjs 进一步了解 prisma。但无法让架构发挥作用。种子不会通过,因为公寓取决于所有者字段,只能由创建的用户填充。

我想创建一个简单的数据库设置,其中用户可以是公寓的所有者,但必须拥有公寓。

一套公寓需要有一个业主,并且可以有多个租户。

非常感谢您在这里伸出援手,否则这只是另一个永远无法克服概念的项目。

架构:

model User {
  userId        String    @id @default(uuid())
  firstName     String?
  lastName      String?
  nickname      String
  email         String    @unique
  password      String
  phoneNumber   String?
  ownerOf       Flat?     @relation("owner")
  ownerOfId     String?   @unique
  flatId        String
  flat          Flat      @relation(fields: [flatId], references: [flatId])
  paidFor    Expense[] @relation("paidBy")
  otherExpenses Expense[]
  updatedAt     DateTime? @updatedAt
  createdAt     DateTime? @default(now())

  @@map("users")
}

model Flat {
  flatId    String    @id @default(uuid())
  name      String
  owner     User?      @relation("owner", fields: [ownerId], references: [userId])
  ownerId   String?    @unique
  flatmates User[]
  expenses  Expense[]
  updatedAt DateTime? @updatedAt
  createdAt DateTime? @default(now())

  @@map("flats")
}

model Expense {
  expenseId   String    @id @default(uuid())
  flatId      String
  flat        Flat      @relation(fields: [flatId], references: [flatId])
  paidBy   User      @relation("paidBy", fields: [paidById], references: [userId])
  paidById String
  expenseFor  User[]
  amount      Float
  updatedAt   DateTime? @updatedAt
  createdAt   DateTime? @default(now())

  @@map("expenses")
}
const users = await prisma.user.create({
    data: {
      firstName: 'Flo',
      lastName: 'Test',
      nickname: 'flo',
      email: '[email protected]',
      password: hash,
      flat: {
        create: {
          name: 'Test Flat',
          owner: {
            connect: {
              users,
            },
          },
        },
      },
    },
  });
typescript orm prisma prisma2
2个回答
0
投票

我会开始考虑关系。

  • 公寓和住户之间是什么关系?是1:n。一套公寓可以有多个租户。一个用户只能是一套公寓的租户。
  • 公寓和业主之间是什么关系?从你的架构来看,它是 1:1。 (您可能需要仔细检查一下。一个用户不能拥有多套公寓吗?)

额外要求是:

  • 用户必须是公寓的租户。
  • 公寓必须有业主(来自您的问题文本)。但你的模式不需要这样!这是一个关键点。

owner
公寓仍然是可选的

您可以在您的

schema.prisma
(简化版)中对这些关系进行建模:

model User {
  userId    String @id @default(uuid())
  nickname  String
  flatId    String
  flat      Flat   @relation(fields: [flatId], references: [flatId], name: "tenantRelation")
  ownedFlat Flat?  @relation(name: "ownerRelation")

  @@map("users")
}

model Flat {
  flatId  String  @id @default(uuid())
  name    String
  ownerId String? @unique
  owner   User?   @relation(fields: [ownerId], references: [userId], name: "ownerRelation")
  tenants User[]  @relation(name: "tenantRelation")

  @@map("flats")
}

无需引入多余的

User.ownerOfId

该模式不能保证每个公寓都有一个所有者,因为

ownerId
是可选的。

如果你接受这个缺陷,你可以像这样创建用户和平面:

await prisma.user.create({
  data: { nickname: "flo", flat: { create: { name: "Test flat" } } },
});

第二步将公寓的所有者设置为用户...

owner
单位应该是必填的

如果您不接受此缺陷,并使

ownerId
成为非可选,则播种确实会变得更加困难,因为您具有循环依赖关系。

在架构中,您只需删除两个问号即可:

model Flat {
  ...
  ownerId String @unique
  owner   User   @relation(fields: [ownerId], references: [userId], name: "ownerRelation")
  ...

然后你必须:

  • 在数据库上设置外键
    DEFERRABLE INITIALLY DEFERRED
    DEFERRABLE INITIALLY IMMEDIATE
    。目前,prisma 无法做到这一点(请参阅:https://github.com/prisma/prisma/issues/8807)。
  • 在同一事务中执行原始查询,如下所示:
import { v4 as uuidv4 } from "uuid";

// ...

  const userId = uuidv4();
  const flatId = uuidv4();

  await prisma.$transaction(async ($tx) => {
    await $tx.$executeRaw`set constraints all deferred;`;
    await $tx.$executeRaw`INSERT INTO users ("userId", "nickname", "flatId") VALUES (${userId}, 'flo', ${flatId});`;
    await $tx.$executeRaw`INSERT INTO flats ("flatId", "name", "ownerId") VALUES (${flatId}, 'Test flat', ${userId});`;
  });

目前需要在您的

previewFeatures = ["interactiveTransactions"]
中使用
schema.prisma

但在走这条路之前,我建议仔细检查您的应用程序将如何真正使用数据库。用户和单位是否总是在同一步骤中创建?


0
投票

您需要先创建

user
,然后在后续操作中创建
connect
flat
。我会将其包装在一个交互式交易中。

© www.soinside.com 2019 - 2024. All rights reserved.