更新 Prisma 中的多对多关系

Posted

技术标签:

【中文标题】更新 Prisma 中的多对多关系【英文标题】:Updating many-to-many relations in Prisma 【发布时间】:2021-12-04 11:09:11 【问题描述】:

我有一组针对皮肤问题的复选框。用户可以在提交前勾选/取消勾选,也就是说每次提交的皮肤问题集可能都不一样。

我在 Prisma 模式中将其建模为“显式”多对多关系。

model User 
  id                 String                 @id @default(cuid())
  name               String?
  nickname           String?                @unique
  ...
  skinConcerns       SkinConcernsForUsers[]
  ...


model SkinConcern 
  id   Int                    @id @default(autoincrement())
  name String                 @unique
  user SkinConcernsForUsers[]


model SkinConcernsForUsers 
  user          User        @relation(fields: [userId], references: [id])
  userId        String
  skinConcern   SkinConcern @relation(fields: [skinConcernId], references: [id])
  skinConcernId Int

  @@id([userId, skinConcernId])

然后,使用 prisma.skinConcern.createMany 为 SkinConcerns 表播种以下值:

"ACNE",
"DRYNESS",
"OILY_SKIN",
"PIGMENTATION",
"REDNESS",
"WRINKLES",

SkinConcerns 在更新变异输入中以字符串数组的形式出现,例如["PIGMENTATION", "REDNESS"].

我想通过 prisma.user.update 查询为用户 (SkinConcernsForUsers) 更新皮肤问题,但这很棘手,因为我不仅要创建 SkinConcerns,而且还必须连接到现有的皮肤问题集。

我试过直接setting skinConcerns in user,喜欢

await prisma.user.update(
  where:  nickname ,
  data: 
    // ... other user data
    skinConcerns: 
      set: [
        
          skinConcern: 
            connect:  name: "PIGMENTATION" ,
          ,
        ,
        
          skinConcern: 
            connect:  name: "REDNESS" ,
          ,
        ,
      ],
    ,
    // ... other user data
  
);

在许多其他事情中,但这当然不是一个正确的论点,并且会因错误而失败

Unknown arg `connect` in data.skinConcerns.update.0.where.connect for type SkinConcernsForUsersWhereUniqueInput. Did you mean `select`?
Argument data for data.skinConcerns.update.0.data is missing.
Unknown arg `connect` in data.skinConcerns.update.1.where.connect for type SkinConcernsForUsersWhereUniqueInput. Did you mean `select`?
Argument data for data.skinConcerns.update.1.data is missing.

有没有办法做到这一点?甚至可以在prisma.user.update 中更新它吗?

我想我可以直接更新SkinConcernsForUsers。在这种情况下,我是否应该删除与用户关联但不在用户输入["PIGMENTATION", "REDNESS"] 中的所有行,然后创建不存在的行?它在棱镜代码中会是什么样子?

【问题讨论】:

【参考方案1】:

首先,我会将您的架构更改为 SkinConcernid 字段不是必需的,并且会在查询中造成复杂性(在尝试连接/断开记录时,您将不必要地需要将每个 name 映射到 id

name 字段足以作为主键,因为它对于某个记录始终是唯一的。

更改后的架构如下所示

model SkinConcern 
  name String                  @id    // name is the new @id. 
  user SkinConcernsForUsers[]


model SkinConcernsForUsers 
  user          User        @relation(fields: [userId], references: [id])
  userId        String
  skinConcern   SkinConcern @relation(fields: [skinConcernName], references: [name])
  skinConcernName String

  @@id([userId, skinConcernName])


您想要执行的查询可以使用SkinConcernsForUsers 模型分两步执行。

    第 1 步:删除用户连接到的现有 SkinConcernsForUsers 记录。这些不再相关,因为您想覆盖之前的选择。

    第 2 步:使用新选项创建新的 SkinConcernsForUsers 记录。

这是代码的样子


// step 1
await prisma.skinConcernsForUsers.deleteMany(
    where: 
        userId: "1",
    ,
);

// step 2
await prisma.skinConcernsForUsers.createMany(
    data: [
        
            userId: "1",
            skinConcernName: "REDNESS",
        ,
        
            userId: "1",
            skinConcernName: "PIGMENTATION",
        ,
    ],
);

【讨论】:

谢谢!实际上,这是我在提出这个问题后立即想到的方法,并且我对使用字符串作为主键进行了一些研究。关于 SO 的一般建议是使用自动递增的整数 ID。我认为在我的情况下性能不是什么大问题,但我担心如果我将来必须更改这些键会很头疼(尽管我认为这不太可能)。无论如何,这将使我的工作变得简单得多,因此现在避免一些头痛可能比担心将来可能出现的头痛更好。 乐于助人:) 顺便说一下,对于另一个表,如果我绝对必须有一个整数 ID 作为主键和一个单独的名称列(如我原来的问题),我应该如何解决这个问题?跨度> 你需要某种映射到name_column -> intenger_ID。使用此映射,您可以找到对应的name_columninteger_ID(假设REDNESS 映射到ID 3)。然后,您更改查询以使用 integer_ID 字段代替 name_column(对于我编写的示例查询,您可以将 skinConcernName 更改为 skinConcernId)。 不幸的是,如果您在代码中手动编写它,就没有真正的方法来“保证”同步。我会考虑编写一个小型测试套件来验证数据是否与数据库同步。但正如我之前所说,如果记录数太多,这将非常难以管理,在这种情况下,我只需使用唯一的 name_column 查询数据库以找到匹配的 integer_ID 主键列。

以上是关于更新 Prisma 中的多对多关系的主要内容,如果未能解决你的问题,请参考以下文章

从 Prisma 中的多对多表中获取元信息

Prisma 多对多关系查询

使用 Prisma 通过关联表查询多对多关系

关于symfony的多对多关联关系的字段更新

EF Core 5.0 - 更新 ASP.NET Core Web API 中的多对多实体

Access中的多对多关系来自单个表