Prisma多对多关系:创建和连接

17
在我的Prisma模式中,我有一个帖子和类别之间的多对多关系。我已经添加了@map选项以匹配Postgres的snake_case命名约定:
model Post {
  id         Int            @id @default(autoincrement())
  title      String
  body       String?
  categories PostCategory[]

  @@map("post")
}

model Category {
  id    Int            @id @default(autoincrement())
  name  String
  posts PostCategory[]

  @@map("category")
}

model PostCategory {
  categoryId Int      @map("category_id")
  postId     Int      @map("post_id")
  category   Category @relation(fields: [categoryId], references: [id])
  post       Post     @relation(fields: [postId], references: [id])

  @@id([categoryId, postId])
  @@map("post_category")
}

我正在尝试创建一个同时具有多个类别的帖子。如果类别存在,我想要将类别与帖子连接。如果类别不存在,我想要创建它。创建部分运作良好,但连接部分存在问题:
  await prisma.post.create({
    data: {
      title: 'Hello',
      categories: {
        create: [{ category: { create: { name: 'News' } } }],
        connect: {
          categoryId_postId: { categoryId: 1, postId: ? }, // This doesn't work, even if I had the postId
        },
      },
    },
  });

我该如何使用现有的模式连接一个新帖子和一个分类?
5个回答

20

你需要的是connectOrCreate

所以像这样做应该可以:

      await prisma.post.create({
        data: {
          title: 'Hello',
          categories: {
            create: [
              {
                category: {
                  create: {
                    name: 'category-1',
                  },
                },
              },
              { category: { connect: { id: 10 } } },
            ],
          },
        },
      });

您还可以在文档这里中阅读更多相关内容。


1
这个表现出奇怪的行为。如果在post_category表中存在{ categoryId: 1, postId: 1 }桥接记录,则会用{ categoryId: 1, postId: new_id }替换桥接记录。换句话说,它会将另一篇文章的类别重新分配给这篇新文章。我不希望在创建新文章时影响任何其他文章。如果该类别不存在,我想创建一个新类别,如果存在,则添加一个新的桥接记录。这里有一个使用隐式关系的createset的好例子:(url to follow),但它不起作用,因为我正在使用显式关系。 - Johnny Oshika
这是我在之前评论中提到的URL链接:https://www.prisma.io/docs/support/help-articles/working-with-many-to-many-relations#implicit-relations那里的示例使用了 tags: { set: [{ id: 1 }, { id: 2 }], create: { name: 'typescript' } },这正是我想要做的,但是似乎无法与我的显式关系配合使用。 - Johnny Oshika
根据您提供的文档,我认为我需要类似这样的东西,但是由于我无法从 categoryId_postId 链接到 category(对格式不好抱歉),所以这会引发异常:connectOrCreate: { create: { category: { create: { name: 'category-1' } }, }, where: { categoryId_postId: { category: { name: 'category-1' }, }, } } - Johnny Oshika
在这种情况下,这应该可以工作:await prisma.post.create({ data: { title: 'title', categories: { create: { category: { connect: { id: 1 } } } }, }, })这将把帖子连接到现有类别,并在联接表中创建关系。 - Ryan

4
这个问题让我很苦恼,所以我在这里分享我的解决方案,希望能帮助其他人。首先,请阅读这篇文章:https://www.prisma.io/docs/concepts/components/prisma-schema/relations/many-to-many-relations
我的模式与问题中的类似,唯一的区别是我有“文件”而不是“帖子”,“标签”而不是“类别”。为了将创建的文件的标签链接到现有的标签,我使用了以下代码:
  await Promise.all(DEFAULT_FILES[2].map(file => prisma.file.create({
    data: {
      ...file,
      user_id: userId,
      parent_id: homeFolder.id,
      tags: {
        create: file.tags?.map(name => ({
          tag: {
            connect: {
              id: tags.find(t => t.name === name)?.id
            }
          }
        }))
      },
    }
  })))

你能告诉我你的解决方案中文件的类型吗?我让它工作了,但不知道输入的类型是什么。我以为它是Prisma.FileCreateInput,但标签没有映射属性。 - Robin-Whg
这是一个名为 seed.ts 的文件,我使用 "seed": "ts-node ./prisma/seed.ts" 命令来执行它。 - TeemuK

2

经过数小时的尝试,我终于为我的使用情况想出了以下解决方案。

park[] <-> exercise[]

// create parks
const parks = await prisma.park.createMany({data: [...]});

// create and connect exercises
const exercises = [...];
await Promise.all(
  exercises.map(async (exercise) => {
    await prisma.exercise.create({
      data: <any>{
        ...exercise,
        parks: {
          connect: parks.map((park) => ({ id: park.id })),
        },
      },
    });
  }),
);

1

0

Explicit many-to-many create/update existing tags read more here

let args =[1,2,3,4]
tags: {
      create: args.tags?.map(tagId=>({
          tag:{
              connect:{
                  id:tagId
              }
          }
      }))
    },
 }


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接