Prisma, how to preserve foreign data? - prisma

It's been a few months since I started prisma and I'm still confused.
In a normal database, foreign key data also exists in table data. However, according to the prisma document, in prisma, the data does not exist at the database level.
So where is it stored? It seems that the things I do "connect:id:1" are stored in the Prisma client. If I delete the prisma dependency and install it again with npm install, will all these relational data be deleted too?? How can I make it as safe as possible????
And it seems too dangerous when I migrate later. what am I misunderstanding?
ADDED
const user = await prisma.user.create({
data: {
email: 'vlad#prisma.io',
posts: {
connect: [{ id: 8 }, { id: 9 }, { id: 10 }],
},
},
include: {
posts: true, // Include all posts in the returned object
},
})
in this case, id 8, id 9, id 10, Where are all these stored? Is there any way to check other than prisma studio or select query? I don't know where it is physically stored. It's not even in the planet scale database.
// In the workbench, the foriegn key is actually saved and can be exported. I don't know how it's not at the database level, but where it is referenced and stored.

Considering this Schema:
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id Int #id #default(autoincrement())
name String
email String #unique
posts Post[]
}
model Post {
id Int #id #default(autoincrement())
title String
published Boolean #default(true)
author User #relation(fields: [authorId], references: [id])
authorId Int
}
There is a one-to-many relationship between User and Posts.
according to the prisma document, in prisma, the data does not exist
at the database level.
Only the relation fields do not exist at the database level, so in this case posts in User model and author in Post model would not exist at database level. But the foreign key exists at the database level, so in this case authorId is actually stored in the database.
Based on the create query you have shared:
in this case, id 8, id 9, id 10, Where are all these stored?
The connect statement in create query is essentially linking the records.
So to elaborate Posts with id 8,9,10 would have the authorId value of the new user record which is created.
So the data is stored in database, you can always check which posts are created by a specific author. You just need to query all the posts which has authorId set to the id which you are querying for.

Related

Create a record and connect it to an existing record prisma client (1 to 1 relation)

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?
The fix:
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});
The explanation:
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.
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.

After upgrading to Prisma2 should Psima1 still be able to create relation in nested create?

I'm attempting to upgrade Prisma from 1 to 2, opting to run prisma1 (1.34.12) along-side Prisma2 (3.4.1)
My problem is that after upgrading the MySQL DB and schema, my prisma1 queries are failing when trying to create nested creates in relation tables.
Example (paraphrased) Prisma2 schema:
model User {
id String #id #default(cuid()) #db.Char(30)
email String #db.MediumText
admin Admin?
}
model Admin {
id String #id #default(cuid()) #db.Char(30)
name String #db.MediumText
userId String #db.Char(30)
user User #relation(fields: [userId], references: [id], onUpdate: Restrict)
}
Example (paraphrased) Prisma1 query:
mutation ($data: UserCreateInput!) {
createuser(data: $data) {
id
email
}
}
variables
{
"data" {
"email": "diety#supremebeing.org"
"admin": {
"create": {
"name": "me"
}
}
}
}
The (paraphrased) error I get:
(conn=3216) Field 'userId' doesn't have a default value
This section of the schema-incompatibilities prisma-upgrade doc seems to indicate that, after updating the schema and MySQL DB via the directions in the work-around, prisma1 should stick the userId into that Admin table based on the foreign key constraint, shouldn't it?
https://www.prisma.io/docs/guides/upgrade-guides/upgrade-from-prisma-1/schema-incompatibilities-postgres#all-non-inline-relations-are-recognized-as-m-n
How might I get my prisma1 queries/mutations to play in the Prisma2-upgraded DB?
Thank you!

Updating many-to-many relations in Prisma

I have a group of checkboxes for skin concerns. Users can check/uncheck them before submitting, which means the set of skin concerns submitted can be different every time.
I modeled it in Prisma schema as an 'explicit' many-to-many relation.
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])
}
Then, SkinConcerns table is seeded with the following values, using prisma.skinConcern.createMany:
"ACNE",
"DRYNESS",
"OILY_SKIN",
"PIGMENTATION",
"REDNESS",
"WRINKLES",
SkinConcerns in Update mutation input comes in the form of array of strings, e.g. ["PIGMENTATION", "REDNESS"].
I want to update the skin concerns for users (SkinConcernsForUsers) from the prisma.user.update query, but it's tricky, since I'm not merely creating SkinConcerns, but have to connect to existing set of skin concerns.
I've tried directly setting skinConcerns in user, like
await prisma.user.update({
where: { nickname },
data: {
// ... other user data
skinConcerns: {
set: [
{
skinConcern: {
connect: { name: "PIGMENTATION" },
},
},
{
skinConcern: {
connect: { name: "REDNESS" },
},
},
],
},
// ... other user data
}
});
among many other things, but of course this is not a correct argument and fails with error
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.
Is there a way to do this? Is it even possible to update this in prisma.user.update?
I guess I could directly update SkinConcernsForUsers. In that case, should I just delete all rows associated to the user that are not in the user input ["PIGMENTATION", "REDNESS"], then create rows that don't already exist? What will it look like in prisma code?
First I would change your schema for SkinConcern. The id field is not necessary and will create complications in queries (you would needlessly need to map each name to id when trying to connect/disconnect records.
The name field is sufficient as the primary key, as it is always unique for a certain record.
The changed schema looks like this
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])
}
The query you want to do can be executed in two steps with the SkinConcernsForUsers model.
Step 1: Remove existing SkinConcernsForUsers records a user is connected to. These are no longer relevant, as you want to overwrite the previous selection.
Step 2: Create new SkinConcernsForUsers records with the new choices.
Here is what the code looks like
// step 1
await prisma.skinConcernsForUsers.deleteMany({
where: {
userId: "1",
},
});
// step 2
await prisma.skinConcernsForUsers.createMany({
data: [
{
userId: "1",
skinConcernName: "REDNESS",
},
{
userId: "1",
skinConcernName: "PIGMENTATION",
},
],
});

Unable to solve Prisma 1 - 1 and 1 - n relation error

I have 3 Prisma2 tables where User can have lots of Sheet and only one Doc
model User {
id Int #default(autoincrement()) #id
firstName String
lastName String
email String #unique
sheets Sheet[]
docs Doc?
}
model Sheet {
id Int #default(autoincrement()) #id
user_sheets Int
User User #relation(fields: [user_sheets], references: [id])
sheetName String
}
model Doc {
id Int #default(autoincrement()) #id
user_doc Int?
User User? #relation(fields: [user_doc], references: [id])
docName String
}
I am using Prisma2 Client like this to get all sheets and docs of the user with specific email id:
import { PrismaClient } from '#prisma/client'
const prisma = new PrismaClient();
const users = await prisma.user.findMany({
where: {
email: email
},
include: {
sheets: true,
docs: true,
}
});
I have already done migrate-save, migrate-up and generate
The error I am getting is this:
PrismaClientValidationError:
Invalid `prisma.user.findMany()` invocation in
webpack-internal:///./pages/api/resume.js:12:47
{
include: {
sheets: true
~~~~~~
docs: true
~~~~~~
}
}
Unknown field `sheets` for include statement on model User.
This model has no relations, so you can't use include with it.
Please help me understand and resolve it as I used the prisma2 doc as well as followed this tutorial: https://www.youtube.com/watch?v=jeHJbYLCgzI
but with no luck the error continues to haunt me.
Issue Resolved Update
After I do prisma migrate save, prisma migrate up and prisma generate, I stopped the server running via terminal and restarted the server after which the model started working. Figured the issue was due to not restarting the server again after Prisma generates a client using new migrations.
Big thank you to #nburk for possible suggestions.

Entity Framework Many to Many and existing data post from angular

Entity Framework from Database First created the Table model classes having many to many relationships in C# WebApi.
Table ACCOUNTS and table METADATA have a many-to-many relationship between them.
I want to add a new entry on ACCOUNTS table and link this entry with some existing entries from METADATA table. How can I do this using AngularJS to post data?
I am sending this data on $http:
var account: {
Title: 'Title',
User: 'User',
METADATA: [
{
Name: 'value1'
},
{
Name: 'value2'
}]
}
The account variable above is based on ACCOUNTS class which is being read by the C# web api using POST and [FromBody] like this:
[HttpPost]
public async Task<IHttpActionResult> Add([FromBody]ACCOUNTS account)
{
db.ACCOUNTS.Add(account);
await db.SaveChangesAsync();
int accountId = account.AccountId;
return Ok(accountId);
}
I am getting an error of primary key violation of existence of value1 and value2 on table METADATA.
This is correct because the values exist in the table.
But actually I want these values to be linked to the "intermediate table" which links ACCOUNTS and METADATA as many-to-many relationship, and not to be added.
Any solution to this scenario?
Before inserting the passed disconnected account object, you need to map the child METADATA objects to existing database entities. For instance, using something like this:
var medataNames = account.METADATA.Select(e => e.Name);
account.METADATA = db.METADATA.Where(e => metadataNames.Contains(e.Name)).ToList();
db.ACCOUNTS.Add(account);
// ...