Combining Different Relations in Prisma ORM - javascript

I am trying to make a create operation that includes different relations. Here is my schema:
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}
enum UserType {
STUDENT
TEACHER
ADMIN // "İdare"
}
model User {
// Default schema for next-auth
id String #unique #id #default(cuid())
name String
image String?
##map("users")
// Custom
type UserType
studentData Student?
teacherData Teacher?
adminData Admin?
TC String #db.Char(11) #unique // TC will be used like a password for any type of user.
}
model Student {
id String #unique #id #default(cuid())
schoolNumber String #db.Char(3) #unique
class Class #relation(fields: [classId], references: [id])
classId String
examResults ExamResult[]
user User #relation(fields: [userId], references: [id])
userId String #unique
}
model Class {
id String #unique #id #default(cuid())
level Int #db.SmallInt
letter String #db.Char(1)
students Student[]
availableExams AvailableExamsOfClasses[]
}
enum TeacherField {
MATH
COMPUTER_SCIENCE
PHYSICS
CHEMISTRY
BIOLOGY
TURKISH
ENGLISH
DEUTSCH
HISTORY
GEOGRAPHY
RELIGION
PHYSICAL_EDUCATION
MUSIC
ART
}
model Teacher {
id String #unique #id #default(cuid())
field TeacherField
exams Exam[]
user User #relation(fields: [userId], references: [id])
userId String #unique
}
enum AdminRole {
PRINCIPAL // Müdür
DEPUTY_PRINCIPAL // Müdür yardımcısı
COMPUTER_SCIENCE_TEACHER // Bilgisayar bilimleri öğretmeni
COUNSELOR // Rehber öğretmen
}
model Admin {
id String #unique #id #default(cuid())
role AdminRole
user User #relation(fields: [userId], references: [id])
userId String #unique
}
model Exam {
id String #unique #id #default(cuid())
createdBy Teacher #relation(fields: [createdById], references: [id])
createdById String
availableClasses AvailableExamsOfClasses[]
answers Json
results ExamResult[]
}
model AvailableExamsOfClasses {
exam Exam #relation(fields: [examId], references: [id])
examId String
class Class #relation(fields: [classId], references: [id])
classId String
##id([examId, classId])
}
model ExamResult {
id String #unique #id #default(cuid())
student Student #relation(fields: [studentId], references: [id])
studentId String
exam Exam #relation(fields: [examId], references: [id])
examId String
result Json
}
I couldn't figure out the syntax i need for creating an exam using Prisma client.
createdBy is the teacher that created the exam.
availableClasses is the list of classes that can access this exam
answers are obviously answers of exam (like {questionNumber: 1, answer: "A"})
results is an array of exam results
I tried this code but it throws errors:
const teacher1 = await prisma.user.findUnique({
where: {
TC: "11111111114",
},
});
const _9D = await prisma.class.findFirst({
where: {
letter: "D",
level: 9,
},
});
const exam = await prisma.exam.create({
data: {
createdBy: teacher1,
answers: [
{ questionNumber: 1, answer: "E" },
{ questionNumber: 2, answer: "B" },
],
results: [],
availableClasses: [_9D],
},
});

I figured it out with the help of these two links (especially the second one):
Prisma Client Reference (connect)
Working with many-to-many-relations
I updated many-to-many relation between availableExams of class model and availableClasses of exam model according to second link. So this is the new schema now:
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}
// Default schema for next-auth
// model Account {
// id String #id #default(cuid())
// userId String #map("user_id")
// type String
// provider String
// providerAccountId String #map("provider_account_id")
// refresh_token String?
// access_token String?
// expires_at Int?
// token_type String?
// scope String?
// id_token String?
// session_state String?
// user User #relation(fields: [userId], references: [id], onDelete: Cascade)
// ##unique([provider, providerAccountId])
// ##map("accounts")
// }
// Default schema for next-auth
// model Session {
// id String #id #default(cuid())
// sessionToken String #unique #map("session_token")
// userId String #map("user_id")
// expires DateTime
// user User #relation(fields: [userId], references: [id], onDelete: Cascade)
// ##map("sessions")
// }
enum UserType {
STUDENT
TEACHER
ADMIN // "İdare"
}
model User {
// Default schema for next-auth
id String #unique #id #default(cuid())
name String
image String?
// In default schema but we will not use
// email String? #unique
// emailVerified DateTime? #map("email_verified_at")
// password String?
// accounts Account[]
// sessions Session[]
##map("users")
// Custom
type UserType
studentData Student?
teacherData Teacher?
adminData Admin?
TC String #db.Char(11) #unique // TC will be used like a password for any type of user.
}
model Student {
id String #unique #id #default(cuid())
schoolNumber String #db.Char(3) #unique
class Class #relation(fields: [classId], references: [id])
classId String
examResults ExamResult[]
user User #relation(fields: [userId], references: [id])
userId String #unique
}
model Class {
id String #unique #id #default(cuid())
level Int #db.SmallInt
letter String #db.Char(1)
students Student[]
availableExams Exam[]
}
enum TeacherField {
MATH
COMPUTER_SCIENCE
PHYSICS
CHEMISTRY
BIOLOGY
TURKISH
ENGLISH
DEUTSCH
HISTORY
GEOGRAPHY
RELIGION
PHYSICAL_EDUCATION
MUSIC
ART
}
model Teacher {
id String #unique #id #default(cuid())
field TeacherField
exams Exam[]
user User #relation(fields: [userId], references: [id])
userId String #unique
}
enum AdminRole {
PRINCIPAL // Müdür
DEPUTY_PRINCIPAL // Müdür yardımcısı
COMPUTER_SCIENCE_TEACHER // Bilgisayar bilimleri öğretmeni
COUNSELOR // Rehber öğretmen
}
model Admin {
id String #unique #id #default(cuid())
role AdminRole
user User #relation(fields: [userId], references: [id])
userId String #unique
}
model Exam {
id String #unique #id #default(cuid())
createdBy Teacher #relation(fields: [createdById], references: [id])
createdById String
availableClasses Class[]
answers Json
results ExamResult[]
}
model ExamResult {
id String #unique #id #default(cuid())
student Student #relation(fields: [studentId], references: [id])
studentId String
exam Exam #relation(fields: [examId], references: [id])
examId String
result Json
}
And I used this syntax for adding a new exam:
const _9D = await prisma.class.findFirst({
where: {
letter: "D",
level: 9,
},
});
const teacher1 = await prisma.user.findUnique({
where: { TC: "11111111114" },
});
const exam = await prisma.exam.create({
data: {
createdBy: {
connect: { id: teacher1.teacherData.id },
},
availableClasses: {
connect: { id: _9D.id },
},
answers: [],
}
});
Wish it helps developers with a similar problem :)

Related

Prisma seed, node says Unique constraint failed on the constraint

im trying to seed my database, but i got this error: Unique constraint failed on the constraint: Figures_table_manufacturerID_key
i try to fix that, but nothing works, here my schema:
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}
model Figures_table {
id String #id #unique
name String #db.LongText
category String #db.LongText
price String #db.LongText
specifications String #db.LongText
releaseInfo String #db.LongText
details String #db.LongText
createdAt DateTime #default(now())
Series Series[]
Images Images[]
Manufacturers Manufacturers[]
serieID Int #unique
manufacturerID Int #unique
}
model Series {
id Int #id #unique #default(autoincrement())
serie String
serieReferenceID Figures_table #relation(fields: [id], references: [serieID])
}
model Manufacturers {
id Int #id #unique #default(autoincrement())
manufacturer String
manufacturerReferenceID Figures_table #relation(fields: [id], references: [manufacturerID])
}
model Images {
id Int #id #unique #default(autoincrement())
link String
figureID String
figureReferenceID Figures_table #relation(fields: [figureID], references: [id])
}
my seed
import { PrismaClient } from "#prisma/client";
import { series, manufacturers, images, figures } from "./data";
async function seed() {
const prisma = new PrismaClient();
await prisma.figures_table.createMany({ data: figures as any });
await prisma.images.createMany({ data: images as any });
await prisma.manufacturers.createMany({ data: manufacturers as any });
await prisma.series.createMany({ data: series as any });
}
seed();
my data file is too long, if you want to see, click:
https://github.com/DanielTrybe/backend-figures/blob/master/prisma/data.js
In the Figures_table you have defined a unique constraint on manufactureID which would mean that each manufacture could only have one figure, but in your seed data there are multiple figures for the same manufacture which is causing this constraint failed error.
If you want to allow multiple images for a manufacturer you would need to remove the #unique constraint
model Figures_table {
id String #id #unique
name String #db.LongText
category String #db.LongText
price String #db.LongText
specifications String #db.LongText
releaseInfo String #db.LongText
details String #db.LongText
createdAt DateTime #default(now())
Series Series[]
Images Images[]
Manufacturers Manufacturers[]
serieID Int #unique
manufacturerID Int
}

How to update an entity using it's foreign key in Prisma?

I have a simple API where authenticated users are allowed to perform CRUD operations on videos. The Video model holds a foreign key called creatorId, which references the User model that created it. I'm trying to update the video by passing the creatorId into the where clause of the Prisma function, however I am getting the following error:
Unknown arg `creatorId` in where.creatorId for type VideoWhereUniqueInput. Available args:
type VideoWhereUniqueInput {
id?: Int
}
Here is my attempt using Express JS.
export const updateVideo = async (req, res, next) => {
const userId = parseInt(req.user.id)
const videoId = parseInt(req.params.id)
try {
// check if video exists or not
const videoToUpdate = await prisma.video.findUnique({
where: {
id: videoId
}
})
// if no video was found throw an error
if (!videoToUpdate)
return next(createError(404, `Video with ID ${req.params.id} does not exist`))
// update the video
const updatedVideo = await prisma.video.update({
// find video that was created by the user
where: {
creatorId: userId
},
// data to change
data: {
...req.body,
}
})
res.status(200).json(updatedVideo)
}
catch (error) {
console.log(error)
next(error)
}
}
And here are my Prisma models:
model User {
id Int #id #default(autoincrement())
email String #unique
name String
password String
img String?
subscriberCount Int? #default(0)
videos Video[]
comments Comment[]
followedBy Follows[] #relation("following")
following Follows[] #relation("follower")
likedVideos Likes[]
}
model Video {
id Int #id #default(autoincrement())
creator User #relation(fields: [creatorId], references: [id])
creatorId Int
title String
description String
videoUrl String?
views Int? #default(0)
tag String? #default("Uncategorized")
likedBy Likes[]
comments Comment[]
}
How can I fix this such that Prisma can do the update query based on the Foreign key? I'm aware there are ways around this issue that don't involve using the Foreign key like this, but I'm just curious as to why my naive solution doesn't work. For example, why would a similar approach work with a primary key but not a foreign one?
Add the #unique attribute to the creatorId field. Prisma only allows unique fields to be specified in where clauses.

How do I create a required 1 to 1 relationship using Prisma?

I want to create a user, with a predefined id and to transactionally create an authorization and profile connected to the user using nested writes. But the typescript compiler complains that I may only create nested objects, if I don't provide the predefined id. Or if I do provide one, I can't create the connected objects.
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "mongodb"
url = env("DATABASE_URL")
}
model User {
id String
emailAddress String
createdAt DateTime #default(now())
updatedAt DateTime #default(now()) #updatedAt
objectId String #id #default(auto()) #map("_id") #db.ObjectId
profile UserProfile #relation(fields: [id], references: [userId])
authentication UserAuthentication #relation(fields: [id], references: [userId])
##unique([id])
##unique([emailAddress])
##map("users")
}
model UserProfile {
firstName String
lastName String
userId String
objectId String #id #default(auto()) #map("_id") #db.ObjectId
user User?
##unique([userId])
##map("user-profiles")
}
model UserAuthentication {
passwordHash String
userId String
objectId String #id #default(auto()) #map("_id") #db.ObjectId
user User?
##unique([userId])
##map("user-auths")
}
context.prisma.user
.create({
data: {
id: userId,
emailAddress: input.emailAddress.toLowerCase(),
profile: {
create: {
userId,
firstName: input.firstName,
lastName: input.lastName
}
},
authentication: {
create: {
userId,
passwordHash
}
},
createdAt,
updatedAt: createdAt
}
})
The type compiler complains with an error saying that either the id or the authentication/profile are Type 'xxx' is not assignable to type 'never'.
How may I go and create all these objects using nested writes with a predefined id?
P.D.: Also apparently, I must repeat the userId on the create object, which I would think would be inferred. I don't know if this is correct.
I appreciate any knowledge you might share.

Prisma update function fails because of incorrect type

I am using Prisma2. The mutation function looks like this:
const updateUserProfileDetails = async (
inputValues, ctx: { session?: SessionContext } = {}
) => {
const profile = await db.user.update({
where: { id: ctx.session!.userId },
data: {
profile: {
update: {
aboutMe: "this is a random message for about me.", // type error is displayed here
location: "London, UK", // same type error here
profession: "rubber duck", // same type error here
},
},
},
});
return profile;
};
However, on aboutMe, location, profession props, typescript is screaming:
Type 'string' is not assignable to type 'NullableStringFieldUpdateOperationsInput | undefined'.ts(2322)
The relevant Schema looks like this:
model User {
id Int #default(autoincrement()) #id
createdAt DateTime #default(now())
updatedAt DateTime #updatedAt
firstName String?
lastName String?
email String #unique
hashedPassword String?
role String #default("user")
sessions Session[]
profile Profile?
}
model Profile {
id Int #default(autoincrement()) #id
aboutMe String?
location String?
profession String?
user User #relation(fields:[userId], references: [id])
userId Int
}
Versions:
#prisma/cli: 2.6.0 => 2.6.0
#prisma/client: 2.6.0 => 2.6.0
I have been unable to find (in my search through the folders), the definition of NullableStringFieldUpdateOperationsInput. What am I doing wrong?
Could you update #prisma/cli and #prisma/client to 2.7.1? It works fine in the latest version. I have tried it and TS doesn't complain here and the query works fine as well.

prisma2: How to filter by a non required field in prisma.user.findMany()?

This is my prisma datamodel:
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
model User {
id Int #default(autoincrement()) #id
email String #unique
name String
password String
emailVerified Boolean #default(false)
emailToken String?
emailTokenExpiry Float?
createdAt DateTime #default(now())
updatedAt DateTime #updatedAt
}
here, emailToken is an optional field. but I want to filter by this field.
something like this:
await prisma.user.findMany({where: {emailToken: emailToken}})
But, prisma allows only required fields to filter that. Is there any way to achieve this?
I think it is possible to filter by optional fields.
for example, this snippet works for me.
const [user] = await prisma.user.findMany({
where: {
emailToken: args.emailToken,
emailTokenExpiry: {
gte: Date.now() - 3600000,
},
},
});

Categories

Resources