I am trying to associate two models "Note" and "Resource" using sequelize. However, targetKey is not working as expected.
Note modal :
module.exports = function(sequelize, DataTypes) {
return sequelize.define('note', {
NoteID: {
type: DataTypes.INTEGER(11),
allowNull: false,
primaryKey: true,
autoIncrement: true
},
Title: {
type: DataTypes.STRING(50),
allowNull: true
},
Note: {
type: DataTypes.STRING(500),
allowNull: false
},
CreatedBy: {
type: DataTypes.INTEGER(11),
allowNull: false,
references: {
model: 'resource',
key: 'ResourceID'
}
},
UpdatedBy: {
type: DataTypes.INTEGER(11),
allowNull: true,
references: {
model: 'resource',
key: 'ResourceID'
}
}
}, {
tableName: 'note'
});
};
Resource modal :
module.exports = function(sequelize, DataTypes) {
return sequelize.define('resource', {
ResourceID: {
type: DataTypes.INTEGER(11),
allowNull: false,
primaryKey: true,
autoIncrement: true
},
FirstName: {
type: DataTypes.STRING(250),
allowNull: false
},
LastName: {
type: DataTypes.STRING(250),
allowNull: false
}
}, {
tableName: 'resource'
});
};
Association:
Resource.belongsTo(Note,{
foreignKey: 'UpdatedBy',
as: 'Resource_Updated_Note'
});
Note.hasOne(Resource,{
foreignKey: 'ResourceID',
targetKey: 'UpdatedBy',
as: 'Note_Updated_By'
});
Resource.belongsTo(Note,{
foreignKey: 'CreatedBy',
as: 'Resource_Created_Note'
});
Note.hasOne(Resource,{
foreignKey: 'ResourceID',
targetKey: 'CreatedBy',
as: 'Note_Created_By'
});
Although I have mentioned the targetKey while association, it is taking PrimaryKey while joining the tables.
Execution.
Note.findAll({
include: [{
model: Resource,
as: 'Note_Updated_By'
}],
where: {
Status: {
[SQLOP.or]: ['Active', 'ACTIVE']
}
}
}).then(function (response) {
callback(response);
});
On basis of the execution, this select query is generated.
SELECT * FROM `note` LEFT OUTER JOIN `resource` AS `Note_Updated_By` ON `note`.`NoteID` = `Note_Updated_By`.`ResourceID`;
Instead of note.NoteID, it should be note.UpdatedBy
As of new version that i am using "sequelize": "^5.8.12"
use sourceKey instead of targetKey for hasOne and hasMany relations works for me.
ModelName.hasOne(ModelName1, {
as: 'SomeAlias',
foreignKey: 'foreign_key',
onDelete: 'NO ACTION',
onUpdate: 'NO ACTION',
sourceKey: 'YOUR_CUSTOM_ASSOCIATION_KEY'
});
You have to set both hasMany({ sourceKey and belongsTo({ targetKey
This is sequelize docs failing us again, each side has a different name, and we have to set both e.g.:
const Country = sequelize.define('Country', {
country_name: { type: DataTypes.STRING, unique: true },
});
const City = sequelize.define('City', {
parent_country: { type: DataTypes.STRING },
city_name: { type: DataTypes.STRING },
});
Country.hasMany(City, { foreignKey: 'parent_country', sourceKey: 'country_name' } )
City.belongsTo(Country, { foreignKey: 'parent_country', targetKey: 'country_name' } )
If you set just the sourceKey, then the query will be wrong, both are needed.
Minimal runnable example:
main.js
#!/usr/bin/env node
const assert = require('assert')
const path = require('path')
const { DataTypes, Sequelize } = require('sequelize')
let sequelize
if (process.argv[2] === 'p') {
sequelize = new Sequelize('tmp', undefined, undefined, {
dialect: 'postgres',
host: '/var/run/postgresql',
})
} else {
sequelize = new Sequelize({
dialect: 'sqlite',
storage: 'tmp.sqlite'
})
}
;(async () => {
const Country = sequelize.define('Country', {
country_name: { type: DataTypes.STRING, unique: true },
});
const City = sequelize.define('City', {
parent_country: { type: DataTypes.STRING },
city_name: { type: DataTypes.STRING },
});
Country.hasMany(City, { foreignKey: 'parent_country', sourceKey: 'country_name' } )
City.belongsTo(Country, { foreignKey: 'parent_country', targetKey: 'country_name' } )
await sequelize.sync({force: true});
await Country.create({country_name: 'germany'})
await Country.create({country_name: 'france'})
await City.create({parent_country: 'germany', city_name: 'berlin'});
await City.create({parent_country: 'germany', city_name: 'munich'});
await City.create({parent_country: 'france', city_name: 'paris'});
const rows = await City.findAll({
where: { parent_country: 'germany' },
include: {
model: Country,
}
});
assert.strictEqual(rows[0].Country.country_name, 'germany')
assert.strictEqual(rows[1].Country.country_name, 'germany')
assert.strictEqual(rows.length, 2)
})().finally(() => { return sequelize.close() })
package.json
{
"name": "tmp",
"private": true,
"version": "1.0.0",
"dependencies": {
"pg": "8.5.1",
"pg-hstore": "2.3.3",
"sequelize": "6.14.0",
"sqlite3": "5.0.2"
}
}
Produced queries as desired:
Executing (default): DROP TABLE IF EXISTS `Cities`;
Executing (default): DROP TABLE IF EXISTS `Countries`;
Executing (default): DROP TABLE IF EXISTS `Countries`;
Executing (default): CREATE TABLE IF NOT EXISTS `Countries` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `country_name` VARCHAR(255) UNIQUE);
Executing (default): PRAGMA INDEX_LIST(`Countries`)
Executing (default): PRAGMA INDEX_INFO(`sqlite_autoindex_Countries_1`)
Executing (default): DROP TABLE IF EXISTS `Cities`;
Executing (default): CREATE TABLE IF NOT EXISTS `Cities` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `parent_country` VARCHAR(255) REFERENCES `Countries` (`country_name`) ON DELETE CASCADE ON UPDATE CASCADE, `city_name` VARCHAR(255));
Executing (default): PRAGMA INDEX_LIST(`Cities`)
Executing (default): INSERT INTO `Countries` (`id`,`country_name`) VALUES (NULL,$1);
Executing (default): INSERT INTO `Countries` (`id`,`country_name`) VALUES (NULL,$1);
Executing (default): INSERT INTO `Cities` (`id`,`parent_country`,`city_name`) VALUES (NULL,$1,$2);
Executing (default): INSERT INTO `Cities` (`id`,`parent_country`,`city_name`) VALUES (NULL,$1,$2);
Executing (default): INSERT INTO `Cities` (`id`,`parent_country`,`city_name`) VALUES (NULL,$1,$2);
Executing (default): SELECT `City`.`id`, `City`.`parent_country`, `City`.`city_name`, `Country`.`id` AS `Country.id`, `Country`.`country_name` AS `Country.country_name` FROM `Cities` AS `City` LEFT OUTER JOIN `Countries` AS `Country` ON `City`.`parent_country` = `Country`.`country_name` WHERE `City`.`parent_country` = 'germany';
notably we have the desired REFERENES:
CREATE TABLE IF NOT EXISTS `Cities` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`parent_country` VARCHAR(255) REFERENCES `Countries` (`country_name`) ON DELETE CASCADE ON UPDATE CASCADE,
`city_name` VARCHAR(255));
and he desired JOIN ON:
ON `City`.`parent_country` = `Country`.`country_name`
with the custom columns.
Tested on SQLite and PostgreSQL 13.5.
Related:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With