Model with Foreign Key already done, sequelize - node.js

I am new to the backend field, I have been looking for solutions on how to make the model of two related tables, One-To-Many relationship of an existing database, but each example confuses me more.
CREATE TABLE my_db.person (
id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(255) NOT NULL
created_at DATETIME,
craeted_by INTEGER, -- account_id
);
CREATE TABLE my_db.account (
id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
email VARCHAR(255) UNIQUE,
password VARCHAR(255),
person_id INTEGER NOT NULL,
role INT NOT NULL
);
This is the Foreign Key that I want to make, the same
ALTER TABLE my_db.account
ADD CONSTRAINT fk_account_person_id
FOREIGN KEY (person_id)
REFERENCES my_db.person (id);
ON DELETE CASCADE
ON UPDATE CASCADE;
I did this but it doesn't work
person.ts
import { DataTypes } from 'sequelize';
import { db } from '../../db/connection';
import { Account } from './account';
export const Person = db.define( 'person', {
id: {
type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true,
autoIncrement: true
},
name: {
type: DataTypes.STRING,
allowNull: false
},
created_at: {
type: DataTypes.DATE,
allowNull: true
},
created_by : {
type: DataTypes.INTEGER,
allowNull: true
}
}, {
timestamps: false,
});
Person.hasMany( Account, {
foreignKey: 'person_id',
onDelete: 'cascade',
onUpdate: 'cascade'
}
account.ts
import { DataTypes } from 'sequelize';
import { db } from '../../db/connection';
import { Person } from './Person';
export const Account = db.define( 'account', {
id: {
type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true,
autoIncrement: true
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true
},
password: {
type: DataTypes.STRING,
allowNull: false
},
person_id: {
type: DataTypes.INTEGER,
allowNull: false
},
role: {
type: DataTypes.INTEGER,
allowNull: false
}
}, {
timestamps: false,
});
Account.belongsTo( Person, {
foreignKey: 'person_id',
onDelete: 'CASCADE',
onUpdate: 'CASCADE'
}
Official Documentation
I thought of using the example from the documentation, however, it changes the name to the FK, but it does not show the exact table
enter image description here
enter image description here
clubId or TeamId?
enter image description here
In case the name of the attribute is not needed, does it infer it?
What if I have more than one FK to the same table? How would you infer those two without a constraint?
I did this, but I don't know where to put the constraint, since it already exists.
The database is in production, I cannot make changes to it, and I do not want a new constraint to be created

Related

Creating a foreignKey in sequelize, that is not a integer association

I want to translate this psql table creation query to sequelize:
PSQL:
CREATE TABLE categories
(
id INTEGER NOT NULL PRIMARY KEY,
name CHARACTER VARYING(50) NOT NULL UNIQUE,
description CHARACTER VARYING(100) NOT NULL
);
CREATE TABLE posts
(
id INTEGER NOT NULL PRIMARY KEY,
title CHARACTER VARYING(50) NOT NULL,
content CHARACTER VARYING(100) NOT NULL,
from_category CHARACTER VARYING(50) NOT NULL,
CONSTRAINT fk_from_category
FOREIGN KEY(from_category)
REFERENCES categories(name)
)
Its a simple fk association, with varchar type.
I have read sequelize docs, but i still don't know how to change the relation from primary keys to varchar.
From what i read, this is what you can do with associations:
Post.belongsTo(Category, {
foreignKey: {
onDelete: ...,
onUpdate: ...,
validate: {...},
},
});
and thats all i could find about on youtube too..
I would be really happy if you can help me. I have spent too much time on this already, but i want it to work!
Follow this example using belongsTo:
class Category extends Model { }
Category.init({
id: {
type: DataTypes.INTEGER,
primaryKey: true
},
name: {
type: DataTypes.STRING,
unique: true,
allowNull: false
},
description: {
type: DataTypes.STRING,
allowNull: false
}
}, { sequelize, modelName: 'categories' });
class Post extends Model { }
Post.init({
id: {
type: DataTypes.INTEGER,
primaryKey: true
},
title: {
type: DataTypes.STRING,
allowNull: false
},
content: {
type: DataTypes.STRING,
allowNull: false
},
from_category: {
type: DataTypes.STRING,
allowNull: false
}
}, { sequelize, modelName: 'posts' });
Post.belongsTo(Category, {
foreignKey: 'from_category',
targetKey: 'name'
});
Read more about to understand with more details in the official docs.

Associations with Sequelize Node/Express.js

I got some trouble understanding how associations works with Sequelize. I am working on a project which have almost the same features that Reddit and therefore I am trying to associate the User table to the Post table as a 1:N associations.
User Model:
const { Sequelize, Model, DataTypes } = require ('sequelize');
const db = require('../config/db');
const Post = require('./Post')
class User extends Model{}
User.init({
id: {
type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true,
autoIncrement: true,
},
username:{
type: DataTypes.STRING,
unique: true
},
email:{
type: DataTypes.STRING
},
password:{
type: DataTypes.STRING
},
isAdmin:{
type: DataTypes.BOOLEAN,
defaultValue: false
}
}, {sequelize: db, modelName:'User'}
);
User.hasMany(Post,{as: 'posts', foreignKey: 'id'});
User.sync();
module.exports = User;
Post Model:
const { Sequelize, Model, DataTypes } = require ('sequelize');
const db = require('../config/db');
const User = require('./User');
class Post extends Model{}
Post.init({
id: {
type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true,
autoIncrement: true,
},
text:{
type: Sequelize.STRING
},
image:{
type: Sequelize.STRING
},
likes:{
type: Sequelize.INTEGER,
}
}, {sequelize: db, modelName:'Post'}
)
Post.sync();
module.exports = Post;
When I launch my app, I can see that it mention that post have the foreign key id but still I don't have hany column that link User to Post in my DB. What I am missing?
Executing (default): CREATE TABLE IF NOT EXISTS `Posts` (`id` INTEGER NOT NULL auto_increment , `text` VARCHAR(255), `image` VARCHAR(255), `likes` INTEGER, `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL, PRIMARY KEY (`id`), **FOREIGN KEY (`id`)** REFERENCES `Users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE) ENGINE=InnoDB;
First, you need to indicate the correct value in the foreignKey option, it should be a field like user_id in Post model:
User.hasMany(Post,{as: 'posts', foreignKey: 'user_id'});
Second, you need to move association definitons and sync calls (and cross-refrences of models) from model modules outside. You need to register all models and only after that to define all their associations and the last action would be calling sync methods.
See the question and my answer here to get an idea how to do it.

How can we insert a composite primary key in N:M table using sequelize?

So, we have a table named category_has_serviceprovider that has a composite primary key (idCategory, idUser) and we have a table timeslots with a primary key (idTimeSlots).
What we want to do is: we want to build a N:M table in sequelize using the table category_has_serviceprovider and timeslots. The result should be the table category_x_timeslots that you can see in the next image:
As you can see it has a composite primary key (idTimeSlots, idCategory, idUser), but when we do it in sequelize we get the next image:
So, you can see that it is missing the idUser from category_has_serviceprovider. Can we achieve this in sequelize?
The code that we have for the category_x_timeslots table is:
const Category_has_ServiceProvider = require("../../category_has_serviceProvider/api/category_has_serviceProvider");
const TimeSlots = require("../../timeslots/api/timeslots");
const category_x_TimeSlots = dbconfig.sequelize.define('Category_x_TimeSlots', {
occupied : {
type: dbconfig.Sequelize.BOOLEAN,
allowNull: false
},
experience : {
type: dbconfig.Sequelize.INTEGER,
allowNull: false
}
}, {
freezeTableName: true,
timestamps: false
});
Category_has_ServiceProvider.belongsToMany(TimeSlots, {foreignKey: 'idCategory', through: category_x_TimeSlots});
TimeSlots.belongsToMany(Category_has_ServiceProvider, {foreignKey: 'idTimeSlots',through: category_x_TimeSlots});
category_x_TimeSlots.sync()
module.exports = category_x_TimeSlots;
Tell me if you need to see the other 2 tables implementation.
I think composite foreign keys are only supported at query level and they are not created at the database. As an alternative for the database i would suggest the junction table to have a autoIncrement primary key ( id) and add indexes at the foreign keys columns which should be manually defined.
const Category_has_ServiceProvider = require("../../category_has_serviceProvider/api/category_has_serviceProvider");
const TimeSlots = require("../../timeslots/api/timeslots");
const category_x_TimeSlots = dbconfig.sequelize.define('Category_x_TimeSlots', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
allowNull: false
},
occupied: {
type: dbconfig.Sequelize.BOOLEAN,
allowNull: false
},
experience: {
type: dbconfig.Sequelize.INTEGER,
allowNull: false
},
idCategory: {
type: DataTypes.INTEGER,
allowNull: false
},
idUser: {
type: DataTypes.INTEGER,
allowNull: false
},
idTimeSlots: {
type: DataTypes.INTEGER,
allowNull: false
},
}, {
freezeTableName: true,
timestamps: false,
indexes: [{
unique: true,
fields: ['idCategory', 'idUser', 'idTimeSlots']
}]
});
Category_has_ServiceProvider.belongsToMany(TimeSlots, {foreignKey: ['idCategory', 'idUser'], through: category_x_TimeSlots});
TimeSlots.belongsToMany(Category_has_ServiceProvider, {foreignKey: 'idTimeSlots',through: category_x_TimeSlots});
category_x_TimeSlots.sync()
module.exports = category_x_TimeSlots;

How can I make a composite unique constraint with foreign keys and regular keys in Sequelize?

We have two models, users and items. Under User.js
User = Model.define('User', {
id: {
type: DataType.UUID,
defaultValue: DataType.UUIDV1,
primaryKey: true,
},
});
And under Item.js
Item = Model.define('Item', {
id: {
type: DataType.UUID,
defaultValue: DataType.UUIDV1,
primaryKey: true,
},
});
Here is their association, a user can have many items.
User.hasMany(Items, {
foreignKey: {
allowNull: false,
name: 'itemId',
},
onUpdate: 'cascade',
onDelete: 'cascade',
});
Assume that each user may only have one of each type of item. How do I add a unique constraint for this? The following code does not work.
User.hasMany(Items, {
foreignKey: {
allowNull: false,
name: 'itemId',
unique: 'userItemUnique',
},
onUpdate: 'cascade',
onDelete: 'cascade',
});
Item = Model.define('Item', {
id: {
type: DataType.UUID,
defaultValue: DataType.UUIDV1,
primaryKey: true,
unique: 'userItemUnique',
},
});
You can use migrations for this.
Sequelize-cli provides a methods addConstraint and andIndex which can be used to achieve
From the docs
queryInterface.addConstraint('Users', ['email'],
{ type: 'unique', name: 'custom_unique_constraint_name'
});
If anyone is still following this, I solved this by manually defining the foreign keys in the model where the unique constraint is required (you can still use sequelize association such as .hasMany).
Regarding your own code, I think there might be a confusion when you ask for Assume that each user may only have one of each type of item since you are not defining what is an item type.
I've drafted something with my own understanding and taking into account my previous comment.
User = Model.define('User', {
id: {
type: DataType.UUID,
defaultValue: DataType.UUIDV1,
primaryKey: true,
allowNull: false,
validate: {
isUUID: 1,
},
},
});
Item = Model.define('Item', {
id: {
type: DataType.UUID,
defaultValue: DataType.UUIDV1,
primaryKey: true,
allowNull: false,
validate: {
isUUID: 1,
},
},
type: {
type: DataType.STRING,
unique: 'uniqueUserItemType' // see note 1
}
userId: {
type: DataType.UUID,
references: { // see note 2
model: User,
key: 'id',
},
unique: 'uniqueUserItemType',
}
});
User.hasMany(Item, {
foreignKey: {
allowNull: false,
name: 'itemId',
},
onUpdate: 'cascade',
onDelete: 'cascade',
});
Item.belongsTo(User);
I've also added a belongsTo association as recommended by Sequelize.
[1] More info on composite unique constraint here.
[2] More info on foreign key definition inside of model here.
In my case I did something like this based on Joel Barenco's answer.
const { Model, DataTypes } = require('sequelize');
const User = require('../models/user');
module.exports = function(sequelize){
class Algorithm extends Model {}
UserModel = User(sequelize);//#JA - Gets a defined version of user class
var AlgorithmFrame = Algorithm.init({
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
name: {
type: DataTypes.STRING,
allowNull: false,
},
user_Id: {
type: DataTypes.INTEGER,
references: {
model: UserModel,
key: 'id',
},
}
}, {
sequelize,
modelName: 'Algorithm',
indexes: [{ unique: true, fields: ['name','user_id'] }]
});
return AlgorithmFrame
};
The idea here is to manually create the foreign key, but you can define the unique indexes instead with indexes: [{ unique: true, fields: ['name','user_id'] }]
My tactic also shows how to define the model in a class as well. To call it you simply pass sequelize to it like this, where sequelize is the variable holding all your connection info etc...
const Algorithm = require('../models/algorithm');
const AlogorithmModel = Algorithm(sequelize);
then you can make sure it's created with
await AlogorithmModel.sync({ alter: true });
My user model file is this:
const { Model, DataTypes } = require('sequelize');
module.exports = function(sequelize){
class User extends Model {}
return User.init({
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
name: {
type: DataTypes.STRING,
allowNull: false
},
trading_system_key: {
type: DataTypes.STRING,
allowNull: false
},
}, {
sequelize,
modelName: 'User',
indexes: [{ unique: true, fields: ['trading_system_key'] }]
});
};

Sequelize has and belongs to many

Is there a way to do a polymorphic self-association with a through table (e.g. Collection has and belongs to many Collections)?
Trying to adapt http://docs.sequelizejs.com/manual/tutorial/associations.html#n-m to this scenario:
// Inside collection.js associate
Collection.belongsToMany(Collection, {
through: {
model: models.CollectionItem,
unique: false,
scope: {
collectible: 'collection'
}
},
foreignKey: 'collectibleUid',
constraints: false
});
Where collectionItem.js would look like
const CollectionItem = sequelize.define("CollectionItem", {
uid: {
type: DataTypes.BIGINT,
primaryKey: true
},
collectionUid: {
type: DataTypes.BIGINT,
allowNull: false,
unique: 'collection_item_collectible'
},
order: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 0
},
collectibleUid: {
type: DataTypes.BIGINT,
allowNull: false,
references: null, // Because the column is polymorphic, we cannot say that it REFERENCES a specific table
unique: 'collection_item_collectible'
},
collectible: {
type: DataTypes.STRING,
allowNull: false,
unique: 'collection_item_collectible'
}
}, {
classMethods: {
}
});
It seems Sequelize wants me to name this differently through yet another join / through table, but that would essentially be creating a new table versus just getting a true hasAndBelongsToMany type relationship.
Error: 'as' must be defined for many-to-many self-associations
Try being explicit and add:
as: "CollectionItem"
as does not create a new join table, it just gives the relation a name

Resources