How to link many to many items in Prisma without duplicates - sql

I have many to many relationships between Product and Menu.
model Product {
id Int #id #default(autoincrement())
name String
price Float
description String?
product_category ProductCategory #relation(fields: [category_id], references: [id])
category_id Int
menus MenuProducts[]
created_at DateTime #default(now())
updated_at DateTime #updatedAt
##map("products")
}
model Menu {
id Int #id #default(autoincrement())
name String
products MenuProducts[]
created_at DateTime #default(now())
updated_at DateTime #updatedAt
##map("menus")
}
model MenuProducts {
menu_id Int
menu Menu #relation(fields: [menu_id], references: [id])
product_id Int
product Product #relation(fields: [product_id], references: [id])
created_at DateTime #default(now())
updated_at DateTime #updatedAt
##map("menu_products")
##id([product_id, menu_id])
}
In the menu update, I want to assign many products to the menu.
I've tried like this:
this.prisma.menu.update({where: {id}, data: {...updateMenuDto, products: {connect: {id: 6}}}});
but I received this error:
The required connected records were not found. Expected 1 records to be connected after connect operation on one-to-many relation 'MenuToMenuProducts', found 0.
Anyway, I've tried like this:
this.prisma.menuProducts.create({data:{menu: {connect: {id: id}},product: {connect: {id: 6}}}})
and it worked, but on the second call I saw what the line was duplicated. How I can link multiple products to menu without inserting duplicates?

To remove duplicates from the MenuProducts model you can define a unique constraint on the combination of product_id and menu_id which will restrict having duplication of product and menu id.
model MenuProducts {
menu_id Int
menu Menu #relation(fields: [menu_id], references: [id])
product_id Int
product Product #relation(fields: [product_id], references: [id])
created_at DateTime #default(now())
updated_at DateTime #updatedAt
##map("menu_products")
##id([product_id, menu_id])
##unique([product_id, menu_id])
}
Here is a reference of ##unique constraint.

Related

Prisma ORM optional relations for user models

I am using Prisma ORM with PostgreSQL backend for my database, I have users as students and admins and they have slightly different fields so I created a base model User for common info as well as two more models Admin and Student for further unique info but I can't create an admin (a user referencing an admin) without also referencing it to a student, can anybody help with schema structure?
// schema.prisma
enum Role {
SUPERADMIN
ADMIN
STUDENT
}
model User {
id Int #id #default(autoincrement())
citizenId String #unique
hash String
role Role #default(STUDENT)
createdAt DateTime #default(now())
updatedAt DateTime #updatedAt
student Student? #relation(fields: [citizenId], references: [citizenId], map: "user_student_fk")
admin Admin? #relation(fields: [citizenId], references: [citizenId], map: "user_admin_fk")
}
model Admin {
id Int #id #default(autoincrement())
citizenId String #unique
name String
schoolId Int
createdAt DateTime #default(now())
updatedAt DateTime #updatedAt
user User?
school School #relation(fields: [schoolId], references: [id])
}
model Student {
id Int #id #default(autoincrement())
citizenId String #unique
name String
classId Int
createdAt DateTime #default(now())
updatedAt DateTime #updatedAt
user User?
class Class #relation(fields: [classId], references: [id])
results TestResult[]
}
// ...

Prisma throwing error: Ambiguous relation detected

I'm using prisma and trying to model a referral table for my Postgres database. Not sure if the db schema is correct, but I have a table with a referId, userId: 1 to 1, and referredUserId: 1 to many.
model Referral {
id Int #id #default(autoincrement())
createdAt DateTime #default(now())
updatedAt DateTime #default(now())
referId String // Auto Generate Random String
userId Int #unique
user User #relation(fields: [userId], references: [id])
referredUsersId Int[]
referredUsers User[] #relation(fields: [referredUsersId], references: [id])
}
I'm not sure exactly how to reference these in the User model. I tried
Referral Referral?
UsersReferred Referral[]
But I get an error
Error validating model "User": Ambiguous relation detected
What's the correct way to model a referral table and how can I do it in prisma?
There are multiple relations between the same two models so you would need to specify the name for the relations.
model User {
id String #id
Referral Referral? #relation("UserReferral")
UsersReferred Referral[] #relation("ReferredUsers")
}
model Referral {
id Int #id #default(autoincrement())
createdAt DateTime #default(now())
updatedAt DateTime #default(now())
referId String
userId String #unique
user User #relation(fields: [userId], references: [id], name: "UserReferral")
referredUsersId String
referredUsers User #relation(fields: [referredUsersId], references: [id], name: "ReferredUsers")
}
Here's the reference for Disambiguating relations: Reference

prisma introspect generate invalid model

Prisma version : ^2.26.0
Prisma/client : ^2.26.0
#relation error msg in : Error parsing attribute "#relation": The type of the field userId in the model posts is not matching the type of the referenced field id in model users.
Why prisma created invalid model or something wrong with my code after i run npx prisma introspect?
It cause cannot to enter the prisma studio since those error in my code.
Any solution or thought could share to make me know better for this?
Below is my sql:
-- Create a custom type
CREATE TYPE "user_role_enum" AS ENUM ('user', 'admin', 'superadmin');
-- Create a table
CREATE TABLE "users"(
"id" BIGSERIAL PRIMARY KEY NOT NULL,
"name" VARCHAR(255) NOT NULL,
"email" VARCHAR(255) UNIQUE NOT NULL,
"role" user_role_enum NOT NULL DEFAULT('user')
);
-- Create a table
CREATE TABLE "posts"(
"id" BIGSERIAL PRIMARY KEY NOT NULL,
"title" VARCHAR(255) NOT NULL,
"body" TEXT,
"userId" INTEGER NOT NULL,
FOREIGN KEY ("userId") REFERENCES "users"("id")
);
-- Create data
INSERT INTO "users" ("name", "email", "role")
VALUES('John Doe', 'john#email.com', 'admin'),
('jane Doe', 'jane#email.com', 'admin'),
('Ahmed Hadjou', 'ahmed#hadjou.com', 'user');
INSERT INTO "posts" ("title", "body", "userId")
VALUES('Hello World!!', 'Hey guys, I see a rainbow through this prisma :D', 1),
('So do I', 'It looks cooool!!!', 2),
('It does', 'Yeahhh', 1);
Generate for model from prisma introspect
model posts {
id BigInt #id #default(autoincrement())
title String #db.VarChar(255)
body String?
userId Int
users users #relation(fields: [userId], references: [id]) // Error parsing attribute "#relation": The type of the field `userId` in the model `posts` is not matching the type of the referenced field `id` in model `users`.
}
model users {
id BigInt #id #default(autoincrement())
name String #db.VarChar(255)
email String #unique #db.VarChar(255)
role user_role_enum #default(user)
posts posts[] // error in here
}
enum user_role_enum {
user
admin
superadmin
}
Just change Int to BigInt userId in Post Model , it will work

SQL Constraint Prevent Book From Being Loaned Twice

I am writing a Library database and have the following tables:
Copy (copies of books)
Id int
ISBN nvarchar(13)
Purchase_Date datetime
Customer
Id int
First_Name nvarchar(255)
Last_Name nvarchar(255)
Address nvarchar(255)
Membership_Joined_Date datetime
Membership_Expiry_Date datetime
Loan
Id int
CopyId int
CustomerId int
Loan_Date datetime
Loan_Expiry_Date datetime
Extension_Date datetime (nullable)
Return_Date datetime (nullable)
The copyId and customerId fields are foreign keys from the Copy and Customer tables respectively.
Is there an expression I can write against the loans table that would prevent me from loaning the same book twice? I use a null Return_Date in the Loan table to check if a book is already loaned out. Any help appreciated.
Many databases (including SQL Server) support filtered indexes. You can create a filtered unique index:
create unique index unq_loan_copyid
on loan(copyid) where Loan_Expiry_Date is null;

How do I count the value from one table and then display the same value in another table?

I want to count the total no of Products for a particular shopping list and then display the total count of products for that shopping list according to its id in the Shopping List table.I have created a reference for shopping_list in the product table
My Shopping list table is as follows:-
id serial NOT NULL,
shopping_list_name character varying(255),
shopping_list_status character varying(255) DEFAULT 'OPEN'::character varying,
total_items character varying(255) DEFAULT 0,
created_at timestamp without time zone NOT NULL,
updated_at timestamp without time zone NOT NULL,
deleted integer NOT NULL DEFAULT 0,
CONSTRAINT shopping_lists_pkey PRIMARY KEY (id)
My Product table is as follows:-
id serial NOT NULL,
shopping_list_id integer NOT NULL,
product_name character varying(255) DEFAULT 'null'::character varying,
product_category character varying(255),
quantity integer,
status character varying(255) DEFAULT 'OPEN'::character varying,
deleted integer NOT NULL DEFAULT 0,
CONSTRAINT products_pkey PRIMARY KEY (id)
Can anyone Please Help me:-
So you will have something like
class ShoppingList < ActiveRecord::Base
has_many :products
end
class Product < ActiveRecord::Base
belongs_to :shopping_list
end
Then in order to get the number of products for a shopping list
#my_shopping_list.products.count
And to get the quantity
#my_shopping_list.products.sum(:quantity)
You can have some kind of cache on your shopping list, so lets say that you have an integer field named products_quantity on your ShoppingList, which by default is 0 (make sure to make it 0 by default, not nil).
Then on your Product model, you can do something like
class Product < ActiveRecord::Base
after_create :update_shopping_list
def update_shopping_list
shopping_list.update_attribute(:products_quantity, shopping_list.products_quantity + self.quantity)
end
end
In that case you don't have to execute the query SELECT SUM(quantity) FROM PRODUCTS WHERE shopping_list_id = ?
I'm not sure if I answer your second question, but if you have more questions or if you were trying to ask something else, just let me know.