Reputation: 509
I'm making a Next JS application with prisma and postgres.
I have 2 tables: User and Profile
Their prisma schema structure is as follows:
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[]
}
I'm also using next-auth
for the authentication part of this application. So when a user signs up then upon his email verification, next-auth itself adds the user's record to the User table.
Till here, there's no issue.
Then, when the user opens his dashboard for the first time, then he's shown a form to fill, upon submission of that form, a record needs to be inserted in the Profile table. As the Profile and User table's are linked, they also need to be connected.
So when the user submits profile details form, I do this:
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);
}
But upon running this code, I get the error:
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'
}
How can this be solved? I even tried it the other way (i.e. updating the existing User and creating the Profile record connected to it):
const user = await prisma.user.update({
where: {
email: req.body.email,
},
data: {
profile: {
create: {
// data fields here... (without the user field)
},
},
},
});
But this also gives the same error...
I want to understand why the error comes. Is this not the correct way to create a record for a 1 to 1 relation using prisma-client?
Upvotes: 2
Views: 5123
Reputation: 25988
I think you need to remove @default(cuid())
from the Profile's userId
field definition.
model Profile {
//...
// foreign keys
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
userId String // relation scalar field (used in the `@relation` attribute above)
//...
}
And also get rid of include: { user: true }
:
const profile = await prisma.profile.create({ data: newProfileData});
Profile's user
and userId
fields don't directly translate to actual columns on the db but are fields that let Prisma handle the link between the relations. It ends up translated to PostgreSQL's
create table profile(
--...
userId text references user (id),
--...
);
And later Prisma will populate that field with your User's id
when you issue a user:{connect:{id:'1'}}
. What could've happened is when you used @default(cuid())
in userId
field definition, you interfered with that process. Now the column ends up as
userId text default gen_random_uuid() references user (id)
and whenever you create a Profile, a new row gets entered without specifying your own userId
(which Prisma probably attempts to do before it'll try to link your User), a random id gets generated that doesn't correspond to any existing User, which violates the reference constraint.
It's that and/or your usage of include: { user: true }
messes something up spawning a separate, new user, even though you tried to link your Profile to an existing one. But I would expect that to be just an unwanted side-effect making your code spawn a useless User object and row each time you create a Profile.
Once you get rid of the @default(cuid())
you can also just spawn a standalone, unlinked Profile and then link it to the appropriate User later with an update statement.
Upvotes: 4
Reputation: 424983
Merge the two tables into one, something like:
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[]
}
If you absolutely must have a Profile relation, create a database view:
create view Profile as
select
id,
isAdmin,
name,
email,
createdAt,
updatedAt,
userId
from user
and map it as a read only relation, but I can’t see the point.
Upvotes: -1