创建记录并将其连接到现有记录棱镜客户端(1 对 1 关系)

Posted

技术标签:

【中文标题】创建记录并将其连接到现有记录棱镜客户端(1 对 1 关系)【英文标题】:Create a record and connect it to an existing record prisma client (1 to 1 relation) 【发布时间】:2022-01-08 19:17:45 【问题描述】:

我正在使用 prisma 和 postgres 制作一个 Next JS 应用程序。 我有 2 个表:UserProfile 它们的棱镜模式结构如下:

model User 
  id            String    @id @default(cuid())
  name          String?
  email         String?   @unique
  emailVerified DateTime?
  image         String?

  // foreign keys
  sessions      Session[]
  profile       Profile?


model Profile 
  id                Int      @id @default(autoincrement())
  isAdmin           Boolean  @default(false)
  firstName         String
  lastName          String
  email             String   @unique
  phone             String
  address           String
  gender            String
  image             Bytes
  guardianName1     String
  guardianPhone1    String
  guardianRelation1 String
  guardianName2     String?
  guardianPhone2    String?
  guardianRelation2 String?
  guardianName3     String?
  guardianPhone3    String?
  guardianRelation3 String?
  createdAt         DateTime @default(now())
  updatedAt         DateTime @updatedAt

  // foreign keys
  user              User     @relation(fields: [userId], references: [id], onDelete: Cascade)
  userId            String   @default(cuid()) // relation scalar field (used in the `@relation` attribute above)

  requests      Request[]

我还使用next-auth 作为此应用程序的身份验证部分。因此,当用户注册并通过电子邮件验证时,next-auth 本身会将用户的记录添加到 User 表中。 到这里为止,没有问题。

然后,当用户第一次打开他的仪表板时,他会看到一个要填写的表单,提交该表单后,需要在 Profile 表中插入一条记录。由于 Profile 和 User 表是链接的,因此它们也需要连接。

所以当用户提交个人资料详细信息表单时,我会这样做:

try 
    const newProfileData = 
        // other fields data here...
        user: 
            connect:  id: '1'  // where User table already has a record with - 'id': 1
        
    ;
    const profile = await prisma.profile.create( data: newProfileData, include:  user: true  );
    if(profile)  
        console.log("Created: ", profile);
        res.status(200).json( msg: 'Successfully Created Profile!' ); 
    

catch(err)

    console.log(err);

但是在运行这段代码时,我得到了错误:

The change you are trying to make would violate the required relation 'ProfileToUser' between the `Profile` and `User` models.
...
code: 'P2014',
clientVersion: '2.30.3',
meta: 
    relation_name: 'ProfileToUser',
    model_a_name: 'Profile',
    model_b_name: 'User'

如何解决? 我什至尝试了另一种方式(即更新现有用户并创建与其连接的个人资料记录):

const user = await prisma.user.update(
    where: 
        email: req.body.email,
    ,
    data: 
        profile: 
            create: 
                // data fields here... (without the user field)
            ,
        ,
    ,
);

但这也会产生同样的错误... 我想了解为什么会出现错误。这不是使用 prisma-client 为 1 对 1 关系创建记录的正确方法吗?

【问题讨论】:

1:1 的关系总是有问题的,在这种情况下完全没有必要。 profile 中唯一不在 user 中的属性是 isAdmin 列。当然,除非您允许列 nameemail(两者都存在)不同,否则您会遇到另一组问题。 不,实际上 Profile 表的列比提到的要多得多(我没有写所有这些,而是​​写了 - // other attributes here 您是否尝试过将您的user: connect: id: '1' 换成简单的userId: '1' 并使用include: user: false ?我想知道您对userId String @default(cuid()) 的定义是否会在您发出prisma.profile.create() 时强制它生成一个与任何现有User 都不对应的新随机ID。当您在创建过程中指定include: user: true 时,它是否会尝试创建另一个新用户,并自动生成另一个id 【参考方案1】:

修复:

我认为您需要从 ProfileuserId 字段定义中删除 @default(cuid())

model Profile 
//...
  // foreign keys
  user      User     @relation(fields: [userId], references: [id], onDelete: Cascade)
  userId    String   // relation scalar field (used in the `@relation` attribute above)
//...

同时也摆脱include: user: true

const profile = await prisma.profile.create( data: newProfileData);

解释:

ProfileuseruserId 字段 don't directly translate to actual columns on the db 但是是让 Prisma 处理关系之间的链接的字段。它最终被翻译成 PostgreSQL 的

create table profile(
    --...
    userId text references user (id),
    --...
);

稍后,当您发出user:connect:id:'1' 时,Prisma 将使用您的用户id 填充该字段。可能发生的情况是,当您在 userId 字段定义中使用 @default(cuid()) 时,您干扰了该过程。现在该列结束为

    userId text default gen_random_uuid() references user (id)

并且每当您创建 个人资料 时,都会输入一个新行,而无需指定您自己的 userId(Prisma 可能会在尝试链接您的 用户 之前尝试这样做strong>),生成的随机 id 与任何现有的 User 都不对应,这违反了引用约束。

就是这样和/或您对include: user: true 的使用会产生一个单独的新用户,即使您尝试将您的个人资料链接到现有用户。但我希望这只是一个不需要的副作用,使您的代码在每次创建 Profile 时生成一个无用的 User 对象和行。

删除@default(cuid()) 后,您还可以生成一个独立的、未链接的个人资料,然后稍后使用更新语句将其链接到适当的用户

【讨论】:

非常感谢!我按照你说的做了,并且成功创建了个人资料记录。【参考方案2】:

将两个表合并为一个,例如:

model User 
  id            String    @id @default(cuid())
  name          String?
  email         String?   @unique
  emailVerified DateTime?
  image         String?
  isAdmin           Boolean  @default(false)
  createdAt         DateTime @default(now())
  updatedAt         DateTime @updatedAt
    
  // foreign keys
  sessions      Session[]


如果您绝对必须拥有 Profile 关系,请创建数据库视图:

create view Profile as
select
  id,
  isAdmin,
  name,
  email,
  createdAt,
  updatedAt,
  userId
from user

并将其映射为只读关系,但我看不到重点。

【讨论】:

我正在使用 next-auth 作为该项目的身份验证部分。因此,当用户注册时,next-auth 会将其记录插入到用户表中。然后当用户第一次打开他的仪表板时,需要用户的其他详细信息,这就是将插入到 Profile 表中的内容。他们不是针对所提供的错误的解决方案,例如如何将记录插入到与另一个具有 1 对 1 关系的表中?喜欢不改变数据库结构?另外,我想了解这个错误的原因

以上是关于创建记录并将其连接到现有记录棱镜客户端(1 对 1 关系)的主要内容,如果未能解决你的问题,请参考以下文章

无法添加插槽并将其连接到按钮

如何启动本地数据库服务器并将其连接到 Java? [复制]

如何在 Windows 8.1 上安装 SDL 并将其连接到 Visual Studio Community 2013

使用 recompose setStatic 并将其连接到 redux (nextjs)

在 UILabel 中处理触摸事件并将其连接到 IBAction

如何将查询参数添加到 BigQuery 并将其连接到 Data Studio?