简体   繁体   English

使用具有一对一关系的 prisma 嵌套创建播种

[英]Seeding with prisma nested create with one-to-one relation

Currently working on a project to learn a bit more about prisma with nestjs.目前正在开展一个项目,以使用 nestjs 了解更多关于 prisma 的信息。 But cant get the schema to work.但不能让架构工作。 The seed wont go through because the flat depends on the owner field, that can only be filled with the created user.种子不会 go 通过,因为平面依赖于所有者字段,只能用创建的用户填充。

I wanted to create a simple database setup where a user can be an owner of a flat, but must have a flat.我想创建一个简单的数据库设置,其中用户可以是公寓的所有者,但必须拥有公寓。

A flat needs to have an owner, and can have multiple tennants.一个公寓需要有一个所有者,并且可以有多个租户。

Would be very thankful for a helpful hand here, otherwise its just another project that will never get over concepts.非常感谢这里的帮助,否则它只是另一个永远不会克服概念的项目。

Schema:架构:

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: 'test@test.de',
      password: hash,
      flat: {
        create: {
          name: 'Test Flat',
          owner: {
            connect: {
              users,
            },
          },
        },
      },
    },
  });

You will need to create the user first and then connect the flat in a subsequent operation.您需要先创建user ,然后在后续操作中connect flat I would wrap this in an interactive transaction .我会将其包装在交互式事务中。

I would start thinking in terms of relations.我会开始考虑关系。

  • What is the relation between a flat and its tenants?公寓与租户之间的关系是什么? It's 1:n.现在是 1:n。 One flat can have multiple tenants.一套公寓可以有多个租户。 A user can only be tenant in one flat.一个用户只能是一个单位的租户。
  • What is the relation between a flat and its owner?公寓和业主之间是什么关系? From your schema it is 1:1.从您的架构来看,它是 1:1。 (You might want to double check that. Can't a user own multiple flats?) (您可能需要仔细检查。用户不能拥有多个公寓吗?)

Additional requirements are:附加要求是:

  • A user has to be tenant in a flat.用户必须是公寓的租户。
  • A flat has to have a owner (from your question text).公寓必须有所有者(来自您的问题文本)。 But your schema does not require that.但是您的架构不需要那样。 And it's a critical point.这是一个关键点。

owner of flat remains optional单位的owner仍然是可选的

You can model these relations like this in your schema.prisma (simplified):您可以 model 这些关系在您的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")
}

There is no need to introduce a redundant User.ownerOfId .无需引入多余的User.ownerOfId

That schema does not guarantee that each flat has an owner as ownerId is optional.该模式不保证每个公寓都有一个所有者,因为ownerId是可选的。

If you accept this flaw, you can create user and flat like this:如果你接受这个缺陷,你可以像这样创建用户和平面:

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

and in a second step set the owner of the flat to the user...然后在第二步中将公寓的所有者设置为用户...

owner of flat should be mandatory公寓owner应该是强制性的

If you do not accept this flaw, and make ownerId non-optional, seeding becomes indeed more difficult as you are having a circular dependency.如果您不接受这个缺陷,并且将ownerId非可选,那么播种确实会变得更加困难,因为您有循环依赖。

In the schema, you just remove the two question marks:在架构中,您只需删除两个问号:

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

You would then have to:然后,您必须:

  • Set the foreign keys DEFERRABLE INITIALLY DEFERRED or DEFERRABLE INITIALLY IMMEDIATE on your database.在数据库上设置外键DEFERRABLE INITIALLY DEFERREDDEFERRABLE INITIALLY IMMEDIATE Currently, prisma cannot do that (see: https://github.com/prisma/prisma/issues/8807 ).目前,prisma 无法做到这一点(参见: https://github.com/prisma/prisma/issues/8807 )。
  • Execute raw queries in the same transaction like this:在同一个事务中执行原始查询,如下所示:
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});`;
  });

which currently requires previewFeatures = ["interactiveTransactions"] in your schema.prisma .目前需要在您的schema.prisma中使用previewFeatures = ["interactiveTransactions"]

But before going down that route, I'd suggest to double check how your app would really use the database.但在走这条路之前,我建议仔细检查您的应用程序将如何真正使用数据库。 Would user and flat always be created in the same step?用户和平面是否总是在同一步骤中创建?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM