I have two models:
'use strict';
const { Model } = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class Company extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
Company.hasOne(models.CompanyModules, {
foreignKey: "company_uuid",
sourceKey: 'uuid',
hooks: true
})
}
};
Company.init({
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
uuid: {
type: DataTypes.STRING,
defaultValue: DataTypes.UUIDV4,
primaryKey: true
},
//.... more fields
}, {
sequelize,
modelName: 'Company',
paranoid: true,
hooks: {
afterDestroy: (instance) => {
instance.getCompanyModules().then(companyModule => companyModule.destroy())
}
}
});
return Company;
};
And the other:
'use strict';
const { Model } = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class CompanyModules extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
CompanyModules.belongsTo(models.Company, {
foreignKey: 'company_uuid',
targetKey: 'uuid',
onDelete: 'CASCADE'
});
}
};
CompanyModules.init({
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
allowNull: false
},
uuid: {
type: DataTypes.STRING,
defaultValue: DataTypes.UUIDV4
},
///... more fields
}, {
sequelize,
modelName: 'CompanyModules',
paranoid: true
});
return CompanyModules;
};
I have the following delete code:
await Company.destroy({
where: { id: req.params.id },
individualHooks: true
})
res.status(200).send();
And the idea is that it "Deletes" (actually updating the deletedOn field) on both models. However I am getting an error on the instance.getCompanyModules(). It says that the function doesn't exist, but that's how I read it's supposed to be done. Am I missing something here?
Try explicitly typing the alias for the children table. Sometimes Sequelize does not infer it. Use as property as follows:
Company.hasOne(models.CompanyModules, {
as: 'CompanyModules',
foreignKey: "company_uuid",
sourceKey: 'uuid',
hooks: true
})
https://sequelize.org/docs/v6/core-concepts/assocs/#defining-an-alias
Related
I have a problem with migrating two tables with relationships. I want to add a foreign key to product.js migration, but it is not working. If I will run the application with await sequelize.sync(); database creating well.
How to fix this issue? I did the same thing with another migrations user and addresses, and it worked as I expected. Appreciate your help.
== 20210124144301-create-product: migrating =======
ERROR: Can't create table database_development.products (errno:
150 "Foreign key constraint is incorrectly formed")
create-category.js migration:
"use strict";
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable("сategories", {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER,
},
name: {
type: Sequelize.STRING,
allowNull: false,
unique: true,
},
description: Sequelize.TEXT,
createdAt: {
allowNull: false,
type: Sequelize.DATE,
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE,
},
});
},
down: async (queryInterface, Sequelize) => {
await queryInterface.dropTable("сategories");
},
};
create-product.js migration:
"use strict";
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable("products", {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER,
},
categoryId: {
type: Sequelize.INTEGER,
allowNull: false,
references: {
model: "categories",
key: "id",
},
},
name: {
type: Sequelize.STRING,
allowNull: false,
},
description: {
type: Sequelize.TEXT,
allowNull: false,
},
price: {
type: Sequelize.DOUBLE(11, 2).UNSIGNED,
defaultValue: 0,
},
createdAt: {
allowNull: false,
type: Sequelize.DATE,
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE,
},
});
},
down: async (queryInterface, Sequelize) => {
await queryInterface.dropTable("products");
},
};
category.js model:
"use strict";
const { Model } = require("sequelize");
module.exports = (sequelize, DataTypes) => {
class Category extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
this.hasMany(models.Product, {
foreignKey: "categoryId",
});
}
}
Category.init(
{
name: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
},
description: DataTypes.TEXT,
},
{
sequelize,
tableName: "categories",
modelName: "Category",
}
);
return Category;
};
product.js model:
"use strict";
const { Model } = require("sequelize");
module.exports = (sequelize, DataTypes) => {
class Product extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
this.belongsTo(models.Category, {
foreignKey: "categoryId",
});
}
}
Product.init(
{
name: {
type: DataTypes.STRING,
allowNull: false,
},
description: {
type: DataTypes.TEXT,
allowNull: false,
},
price: {
type: DataTypes.DOUBLE(11, 2).UNSIGNED,
defaultValue: 0,
},
},
{
sequelize,
tableName: "products",
modelName: "Product",
}
);
return Product;
};
You need to add primary key(id) in your product and category model file also change your model associations .
product.js
"use strict";
const { Model } = require("sequelize");
module.exports = (sequelize, DataTypes) => {
class Product extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
Product.belongsTo(models.Category, {
foreignKey: "categoryId",
});
}
}
Product.init(
{
productId: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: DataTypes.INTEGER
},
name: {
type: DataTypes.STRING,
allowNull: false,
},
description: {
type: DataTypes.TEXT,
allowNull: false,
},
price: {
type: DataTypes.DOUBLE(11, 2).UNSIGNED,
defaultValue: 0,
},
categoryId: {
type: DataTypes.INTEGER
}
},
{
sequelize,
tableName: "products",
modelName: "Product",
}
);
return Product;
};
Category.js
"use strict";
const { Model } = require("sequelize");
module.exports = (sequelize, DataTypes) => {
class Category extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
Category.hasMany(models.Product, {
foreignKey: "categoryId",
});
}
}
Category.init(
{
categoryId: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: DataTypes.INTEGER
},
name: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
},
description: DataTypes.TEXT,
},
{
sequelize,
tableName: "categories",
modelName: "Category",
}
);
return Category;
};
the foreign key must be the same type of the primary key on the other table
I'm trying to bulk insert with associations,
I have this 'Song' model which have one to many relationships with 'Genre' and 'Language' defined with the migrations CLI.
Song:
module.exports = (sequelize, DataTypes) => {
class Song extends Model {
static associate(models) {
// define association here
Song.hasMany(models["Language"])
Song.hasMany(models["Genre"])
}
};
Song.init({
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true
},
name: DataTypes.STRING,
energy: {type: DataTypes.FLOAT, allowNull: false},
valence: {type: DataTypes.FLOAT, allowNull: false}
}, {
sequelize,
modelName: 'Song',
timestamps: true
});
return Song;
};
Language:
module.exports = (sequelize, DataTypes) => {
class Language extends Model {
static associate(models) {
// define association here
models["Language"].belongsTo(models["Song"])
}
};
Language.init({
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true
},
name: DataTypes.STRING
}, {
sequelize,
modelName: 'Language',
indexes: [{unique: true, fields: ['name']}]
});
return Language;
};
Genre:
module.exports = (sequelize, DataTypes) => {
class Genre extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
// define association here
models["Genre"].belongsTo(models["Song"])
}
};
Genre.init({
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true
},
name: DataTypes.STRING
}, {
sequelize,
modelName: 'Genre',
indexes: [{unique: true, fields: ['name']}]
});
return Genre;
};
I'm trying to bulk insert songs with languages and genres like this:
Song.bulkCreate(songs, {
include: [Genre,Language]
}).then(() => {
const result = {
status: "ok",
message: "Upload Successfully!",
}
res.json(result);
});
each song in the songs array is structured like this:
{
name: "abc",
genres: [{name: "abc"}],
languages: [{name: "English"}],
energy: 1,
valence: 1
}
I'm ending up with a full songs table but genres and languages are empty
What am I doing wrong?
Thanks.
Just in case anyone else got here from a search, starting from version 5.14
Sequelize added the option to use include option in bulkCreate as follows:
await Song.bulkCreate(songsRecordsToCreate, {
include: [Genre,Language]
})
Edit 2nd Feb 2023
As none answered above, as of v5.14.0 the include option is now available on bulkInsert.
Unfortunately bulkCreate does not support include option like create do.
You should use create in a cycle inside a transaction.
const transaction = ...
for (const song of songs) {
await Song.create(song, {
include: [Genre,Language]
}, { transaction })
}
await transaction.commit()
or you can use Promise.all to avoid using for.
I have three tables (all associated model classnames use PascalCase)
schools school_codes course
------ ------ ------
id (pk) code (pk) name
name school_id (fk) school_code (fk)
I'm trying to define sequelize relations, so that this Course lookup returns the associated School:
const courseWithSchool = await models.Course.findOne({
include: [{
model: models.School,
required: true,
}],
})
The mysql for this is very simple.
mysql> select c.*, s.* from courses c inner join school_codes sc on c.school_code = sc.code inner join schools s on s.id = sc.school_id;
How do I define the relations in sequelize models (without modifying existing schema)? Thanks!
Here are the model definitions I have:
schools.js
module.exports = (sequelize, DataTypes) => {
const School = sequelize.define('School', {
name: DataTypes.STRING,
}, { underscored: true, freezeTableName: true, tableName: 'schools' })
return School
}
course.js
module.exports = (sequelize, DataTypes) => {
const Course = sequelize.define('Course', {
id: {
type: DataTypes.STRING,
primaryKey: true,
},
name: DataTypes.STRING,
school_code: {
type: DataTypes.STRING,
references: {
model: 'school_codes',
key: 'code',
}
}
}, { underscored: true, freezeTableName: true, tableName: 'courses' })
return Course
}
schoolcode.js
module.exports = (sequelize, DataTypes) => {
const SchoolCode = sequelize.define('SchoolCode', {
code:{
type: DataTypes.STRING,
primaryKey: true,
references: {
model: 'courses',
key: 'school_code'
}
},
school_id: {
type: DataTypes.INTEGER,
references: {
model: 'schools',
key: 'id',
},
},
}, { underscored: true, freezeTableName: true, tableName: 'school_codes', })
return SchoolCode
}
I'm just looking for the relations to add to the bottom of each model definition - example...
// School.associate = function (models) {
// School.belongsToMany(models.Course, {
// through: 'school_codes',
// foreignKey: 'school_id',
// otherKey: 'code'
// })
// }
We can keep association in its respective model. I prefer to keep association in respective master table rather than mapping table. The idea is to associate source model to target model and its relationship in both direction. For example let us say source model School has one SchoolCode target model and its reverse relation
//school.model.js
module.exports = (sequelize, DataTypes) => {
const School = sequelize.define('school', {
name: DataTypes.STRING,
}, { underscored: true, freezeTableName: true, tableName: 'schools' })
School.associate = function ({SchoolCode, Course}) {
School.hasOne(SchoolCode, {
foreignKey: 'school_id',
})
SchoolCode.belongsTo(School, {foreignKey: 'school_id'})
School.belongsToMany(Course, { through: SchoolCode , foreignKey : 'school_id'}); //added new
}
return School;
}
//course.model.js
module.exports = (sequelize, DataTypes) => {
const Course = sequelize.define('course', {
id: {
type: DataTypes.STRING,
primaryKey: true,
},
name: DataTypes.STRING,
school_code: {
type: DataTypes.STRING,
references: {
model: 'school_codes',
key: 'code',
}
}
}, { underscored: true, freezeTableName: true, tableName: 'courses' })
Course.associate = function ({SchoolCode, School}) {
Course.hasMany(SchoolCode, {
foreignKey: 'code',
})
Course.belongsToMany(School, { through: SchoolCode, foreignKey : 'code'}); //added new
}
return Course;
}
Finally the third model of SchoolCode (Mapping table).
Note that we don't have to add a reference school_code. It is a primaryKey code of same table. We use references mainly to define the foreign keys, no need for reverse definition here.
Hence commented that part from code below.
module.exports = (sequelize, DataTypes) => {
const SchoolCode = sequelize.define('SchoolCode', {
code:{
type: DataTypes.STRING,
primaryKey: true,
// references: {
// model: 'courses',
// key: 'school_code'
// }
},
school_id: {
type: DataTypes.INTEGER,
references: {
model: 'school',
key: 'id',
},
},
}, { underscored: true, freezeTableName: true, tableName: 'school_codes', })
return SchoolCode
}
References : https://sequelize.org/master/manual/assocs.html
You can define relations like
SchoolCode.belongsTo(School, { foreignKey: 'school_id', targetKey: 'id' });
Course.belongsTo(SchoolCode, { foreignKey: 'school_code', targetKey: 'code' });
I am trying to create one to many foreign key constraint between two tables. A client can have many environments.
Here is my snippet in the model.
Client.associate = function(models) {
Client.hasMany(models.Enviornment, {as: 'enviornments', foreignKey: 'clientId'})
};
The solution is here.
The client has many environments. (one to many association )
Here is a code snippet for a model client.
module.exports = (sequelize, DataTypes) => {
let client = sequelize.define ('client', {
id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true, field: 'id' },
...
}, {
associate: models => {
client.hasMany (models.enviornment, {
foreignKey: { name: 'client_id', allowNull: false }
});
},
});
return client;
};
Here is a code snippet for a model enviornment.
module.exports = (sequelize, DataTypes) => {
let enviornment = sequelize.define ('enviornment', {
id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true, field: 'id' },
...
}, {
associate: models => {
enviornment.belongsTo (models.client, {
foreignKey: { name: 'client_id', allowNull: false }
});
},
});
return enviornment;
};
I am trying to update with association using sequelize.js.
I have tried give example on stackoverflow namely the following links:
Sequelize update with association
Sequelize update with association
Updating attributes in associated models using Sequelize
all of these links did not get me to the goal i am trying to accomplish.
My model is as follow, I have a country module and a city module. a country has many cities. please refer to the module bellow.
Please advise.
country.js file
module.exports = function (sequelize, DataTypes) {
var country= sequelize.define('COUNTRY', {
COUNTRY_ID: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
COUNTRY_NAME: DataTypes.STRING,
COUNTRY_CURRENCY: DataTypes.STRING
}, {
freezeTableName: true,
classMethods: {
associate: function (models) {
COUNTRY_ID.hasMany(models.CITIES, {
foreignKey: 'COUNTRY_ID'
})
}
},
instanceMethods: {
updateAssociation: function (onSuccess, onError) {
country.findAll({
where: {
COUNTRY_ID: req.params.country_id
},
include: [
{
model: sequelize.import('./cities.js'),
}
]
})
})
.then(country =>{
const updatePromises = country.map(countries =>{
return countries.updateAttributes(req.body);
});
const updatePromisescities = list.CITY.map(cities =>{
return cities.updateAttributes(req.body.CITYs[0]);
});
return sequelize.Promise.all([updatePromises, updatePromisescities ])
}).then(onSuccess).error(onError);
}
}
});
return country;
};
city.js file
module.exports = function (sequelize, DataTypes) {
var CITY = sequelize.define('LIST_CODE', {
CITY_ID: {
type: DataTypes.INTEGER,
primaryKey: true
},
COUNTRY_ID: {
type: DataTypes.INTEGER,
primaryKey: true
}
}, {
freezeTableName: true,
timestamps: false,
classMethods: {
associate: function (models) {
// associations can be defined here
CITY.belongsTo(models.COUNTRY, {
foreignKey: 'COUNTRY_ID'
})
}
}
});
return CITY;
};