Related
I've created two Models User and Gasto(means spent in portuguese), these two tables has an association like One User to Many Gasto, so I'm trying to implement the OnDelete 'CASCADE' to when I delete one User all Gasto from this User has to be deleted together, but my association is not working. Can somebody help me?
My Database Config:
import 'dotenv/config'
import { Options } from 'sequelize'
const config: Options = {
username: process.env.DB_USER ?? 'root',
password: process.env.DB_PASS ?? '123456',
database: 'gastos_app_db',
host: process.env.DB_HOST ?? 'localhost',
port: Number(process.env.DB_PORT) ?? 3002,
dialect: 'mysql',
dialectOptions: {
timezone: 'Z'
},
logging: false
}
module.exports = config
Migration from User, Gasto has the same example.
'use strict'
/** #type {import('sequelize-cli').Migration} */
module.exports = {
async up (queryInterface, Sequelize) {
await queryInterface.createTable('users', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
allowNull: false,
autoIncrement: true,
onDelete: 'CASCADE'
},
username: {
type: Sequelize.STRING,
allowNull: false
},
email: {
type: Sequelize.STRING,
allowNull: false
},
role: {
type: Sequelize.STRING,
allowNull: false
},
password: {
type: Sequelize.STRING,
allowNull: false
}
})
},
async down (queryInterface, Sequelize) {
await queryInterface.dropTable('users')
}
}
My Instance of the Sequelize and configurations.
import { Sequelize } from 'sequelize'
import * as config from '../config/database'
export default new Sequelize(config)
My Model of user and the Association.
import { DataTypes } from 'sequelize'
import db from '.'
import { Gasto } from './Gasto'
export const User = db.define('User', {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
allowNull: false,
primaryKey: true
},
username: {
type: DataTypes.STRING,
allowNull: false
},
email: {
type: DataTypes.STRING,
allowNull: false
},
password: {
type: DataTypes.STRING,
allowNull: false
},
role: {
type: DataTypes.STRING,
allowNull: false
}
}, {
timestamps: false,
tableName: 'users',
underscored: true
})
User.hasMany(Gasto, {
foreignKey: 'userId',
onDelete: 'CASCADE'
})
Gasto.hasOne(User, {
foreignKey: 'id'
})
Gasto Model:
import { DataTypes } from 'sequelize'
import db from '.'
export const Gasto = db.define('Gasto', {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
allowNull: false,
primaryKey: true
},
userId: {
type: DataTypes.INTEGER,
allowNull: false
},
type: {
type: DataTypes.STRING,
allowNull: false
},
value: {
type: DataTypes.INTEGER,
allowNull: false
},
gastoDate: {
type: DataTypes.STRING,
allowNull: false
}
}, {
timestamps: false,
tableName: 'gastos',
underscored: true
})
Gasto Migration
'use strict'
/** #type {import('sequelize-cli').Migration} */
module.exports = {
async up (queryInterface, Sequelize) {
await queryInterface.createTable('gastos', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
allowNull: false,
autoIncrement: true
},
user_id: {
type: Sequelize.INTEGER,
allowNull: false
},
type: {
type: Sequelize.STRING,
allowNull: false
},
value: {
type: Sequelize.STRING,
allowNull: false
},
gasto_date: {
type: Sequelize.STRING,
allowNull: false
}
})
},
async down (queryInterface, Sequelize) {
await queryInterface.dropTable('gastos')
}
}
I've already readed the all documentations, and checked some old projects, but I can't found the right way to fix this.
Your user_id field in your migration to create the gastos table must have a Foreign Key definition inside of it, along with the on update/on delete logic you desire (Cascade).
user_id: {
type: Sequelize.INTEGER,
allowNull: false,
references: {
model: 'users',
key: 'id',
},
onUpdate: 'cascade',
onDelete: 'cascade'
}
You can find an example of this in the sequelize query interface documentation.
I have two tables defined, one for movies, one for characters, these are interconnected, by the MovieCharacter table.
Setting the tables to maintain a one-to-many relationship via belongstomany allows me to create a duplicate relationship, and I'm not getting around it.
I leave my code below, i have some experience with mongoose and nosql db but this is new for me.
Thanks!
charModel.js
const { Model, DataTypes } = require('sequelize');
const CHARACTER_TABLE = 'character';
const CharacterSchema = {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
allowNull: false,
},
name: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
},
age: {
type: DataTypes.BIGINT,
allowNull: false,
},
weight: {
type: DataTypes.BIGINT,
allowNull: false,
},
history: {
type: DataTypes.STRING,
allowNull: false
},
image: {
type: DataTypes.STRING,
allowNull: false
},
};
modelMovie.js
class Character extends Model {
static associate(models){
this.belongsToMany(models.Movie, {
as: "movies",
through: "MovieCharacter",
foreignKey: "characterId",
otherKey: "movieId",
});
}
static config(sequelize){
return {
sequelize,
tableName: CHARACTER_TABLE,
modelName: 'Character',
timestamps: false,
}
}
}
const moment = require("moment");
const { Model, DataTypes } = require("sequelize");
const { GENRES_TABLE } = require("./genre.model");
const MOVIES_TABLE = "movie";
const MovieSchema = {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
allowNull: false,
},
title: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
},
creationDate: {
field: "creation_date",
type: DataTypes.STRING,
get(){
return moment(this.getDataValue('creationDate')).format('DD-MM-YYYY')
},
allowNull: false,
},
rating: {
type: DataTypes.INTEGER,
allowNull: false,
},
image: {
type: DataTypes.STRING,
allowNull: false,
},
type: {
type: DataTypes.STRING,
allowNull: false,
},
genreId: {
field: "genre_id",
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: GENRES_TABLE,
key: "id",
},
onUpdate: "CASCADE",
onDelete: "SET NULL",
},
};
class Movie extends Model {
static associate(models) {
this.belongsToMany(models.Character, {
as: "characters",
through: "MovieCharacter",
foreignKey: "movieId",
otherKey: "characterId",
});
this.belongsTo(models.Genre, {
as: "genre",
});
}
static config(sequelize) {
return {
sequelize,
tableName: MOVIES_TABLE,
modelName: "Movie",
timestamps: false,
};
}
}
module.exports = { MOVIES_TABLE, MovieSchema, Movie };
modelCharMov.js
const { Model, DataTypes} = require('sequelize');
const { CHARACTER_TABLE} = require('./character.model');
const { MOVIES_TABLE} = require('./movies.model')
const MOVIES_CHARACTERS_TABLE = 'movies_characters';
const MoviesCharactersSchema = {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
allowNull: false,
},
movieId:{
field: 'movie_id',
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: MOVIES_TABLE,
key: 'id',
},
onUpdate: 'CASCADE',
onDelete: 'SET NULL'
},
characterId:{
field: 'character_id',
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: CHARACTER_TABLE,
key: 'id'
},
onUpdate: 'CASCADE',
onDelete: 'SET NULL'
}
};
class MovieCharacter extends Model {
static config(sequelize) {
return {
sequelize,
tableName: MOVIES_CHARACTERS_TABLE,
modelName: 'MovieCharacter',
timestamps: false
};
}
}
Add 2 BelongsTo associations on the junction table pointing to the tables that are using it to find one another. That would be my first guess because that is the only thing that would have been different in my code compared to yours if implementing this. If that doesn’t work, more details please sir 🧐
There is a table called employees and the employee id wants to self reference to the employee table when the employee has a team lead.
Team lead is also an employee. So how to self reference using Sequelize?
const Employee = sequelize.define("employee", {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
allowNull: false,
},
first_name: {
type: DataTypes.STRING(45),
allowNull: false,
},
lead_role: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: false,
},
designation: {
type: DataTypes.STRING(100),
allowNull: true,
},
resignation_date: {
type: DataTypes.DATE,
allowNull: true,
},
lead_id: {
type: DataTypes.INTEGER,
allowNull: true,
},
});
this is my employee model associations
Employee.associate = function (models) {
Employee.hasOne(models.user, { foreignKey: "employee_id" });
Employee.hasMany(models.leave_request, {
foreignKey: "employee_id",
});
Employee.belongsTo(models.team, {
foreignKey: "team_id",
});
Employee.belongsTo(models.team_manager, {
foreignKey: "manager_id",
});
Employee.hasMany(models.attendance, {
foreignKey: "employee_id",
});
Employee.belongsTo(models.employee, {
foreignKey: "lead_id",
});
Employee.hasMany(models.employee, {
foreignKey: "lead_id",
});
};
You just need to indicate aliases for both a team lead link and employees link:
Employee.belongsTo(models.employee, {
foreignKey: "lead_id",
as: 'TeamLead'
});
Employee.hasMany(models.employee, {
foreignKey: "lead_id",
as: 'TeamEmployees'
});
And you need to indicate the same aliases in queries:
const employees = await Employee.findAll({
where: {
team_id: teamId
},
include: [{
model: Employee,
as: 'TeamLead'
}]
})
const teamLead = await Employee.findAll({
where: {
id: teamLeadId
},
include: [{
model: Employee,
as: 'TeamLead'
}]
})
I am using sequelize CLI to generate and run db migrations. The issue I am having is the id field set to data type Sequelize.UUID appearing as an autoincrement integer in mysql. Here is my user model and migration:
User Model
'use strict';
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define('User', {
UserName: {type:DataTypes.STRING,unique:true,allowNull:false},
FirstName:{type: DataTypes.STRING,allowNull:true},
LastName: {type:DataTypes.STRING,allowNull:true},
Email: {type:DataTypes.STRING,allowNull:false,unique:true,validate: { isEmail: {msg: "Invalid Email"} }},
Password: {type:DataTypes.STRING,allowNull:false},
Avatar: {type:DataTypes.STRING,allowNull:true},
}, {});
User.associate = function(models) {
User.hasMany(models.RoleUser,
{
foreignKey:'UserId',
as:'userroles',
sourceKey:'id'
}),
User.belongsTo(models.Country,
{
foreignKey:'CountryId',
targetKey:'id'
}),
User.belongsToMany(models.Role,
{
through: 'RoleUser',
foreignkey:'UserId'
})
};
return User;
};
**User Migration file:**
'use strict';
const uuidv4 = require('uuid/v4');
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Users', {
id: {
allowNull: false,
primaryKey: true,
type: Sequelize.UUID
defaultValue:uuidv4()
},
UserName: {
type: Sequelize.STRING
},
FirstName: {
type: Sequelize.STRING
},
LastName: {
type: Sequelize.STRING
},
Email: {
type: Sequelize.STRING
},
Password: {
type: Sequelize.STRING
},
Avatar: {
type: Sequelize.STRING
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Users');
}
};
AFTER MIGRATION, THE HIS FIELD IS CONVERTED TO INT AUTOINCREMENT IN MYSQL:
id: {
allowNull: false,
primaryKey: true,
type: Sequelize.UUID
defaultValue:uuidv4()
},
Any pointer as to why this is happening? Please assist. Even the associations seem not to be formed at all as foreign keys are of type Sequelize.UUID
I fixed the problem by adding the id field on model with primary key property set to true.
id: {
type:DataTypes.UUID,
allowNull:false,
unique:true,
primaryKey:true
},
Its like sequelize will automatically generate id field of type INTEGER AUTOINCREAMENT if the model does not have a field with primary key set to true.
I have two models. User and Manager
User Model
const UserMaster = sequelize.define('User', {
UserId: {
type: DataTypes.BIGINT,
allowNull: false,
primaryKey: true,
autoIncrement: true
},
RelationshipId: {
type: DataTypes.STRING,
allowNull: true,
foreignKey: true
},
UserName: {
type: DataTypes.STRING,
allowNull: true
}
})
Manager model
const Manager = sequelize.define('Manager', {
ManagerId: {
type: DataTypes.BIGINT,
allowNull: false,
primaryKey: true,
autoIncrement: true
},
RelationshipId: {
type: DataTypes.STRING,
allowNull: true,
foreignKey: true
},
MangerName: {
type: DataTypes.STRING,
allowNull: true
}
})
Models are minified to simplyfy the problem
Associations..
User.belongsTo(models.Manager, {
foreignKey: 'RelationshipId',
as: 'RM'
});
Manger.hasMany(model.User, {
foreignKey: 'RelationshipId',
as: "Users"
})
So, on user.findAll()
var userObject = models.User.findAll({
include: [{
model: models.Manager,
required: false,
as: 'RM',
attributes: ['ManagerName']
}]
});
I get the following.
userObject = [{
UserId: 1,
RelationshipId: 4545,
UserName: 'Jon',
RM: {
ManagerName: 'Sam'
}
},
{
UserId: 2,
RelationshipId: 432,
UserName: 'Jack',
RM: {
ManagerName: 'Phil'
}
},
...
]
How can I move 'ManagerName' attribute from Manager model (associated as RM) to UserObject?
Is it possible to somehow load attributes from eagerly-loaded models without nesting them under a separate object?
I expected the resulting Object to look Like the object
Expected Object --
userObject = [{
UserId: 1,
RelationshipId: 4545,
UserName: 'Jon',
ManagerName: 'Sam' // <-- from Manager model
},
{
UserId: 2,
RelationshipId: 432,
UserName: 'Jack',
ManagerName: 'Phil' // <-- from Manager model
},
...
]
Thank you.
Adding raw: true along with attributes option worked to get the desired object format.
So,
var userObject = models.User.findAll({
raw:true,
attributes: {
include: [Sequelize.col('RM.ManagerName'), 'ManagerName']
},
include: [{
model: models.Manager,
required: false,
as: 'RM',
attributes: []
}]
});