Why do I have twice the foreign keys using Sequelize - node.js

I'm trying to setup a simple blog application, I have the following schema definition:
Here is User:
module.exports = function(sequelize, DataTypes) {
var User = sequelize.define("User", {
id: {type: DataTypes.BIGINT, autoincrement:true, primaryKey: true},
firstName: {type: DataTypes.STRING},
lastName: {type: DataTypes.STRING},
nickName: {type: DataTypes.STRING},
email: {type: DataTypes.STRING, unique: true, comment: "Unique "},
password: {type: DataTypes.STRING},
salt: {type: DataTypes.STRING},
enabled: {type: DataTypes.BOOLEAN, allowNull: false, defaultValue: true}
}, {
freezeTableName: true,
classMethods: {
associate: function (models) {
User.hasMany(models.Article, {as: "Articles", constraints: false});
User.hasMany(models.Comment, {as: "Comments", constraints: false});
}
}
});
return User;
};
Here is Article:
module.exports = function(sequelize, DataTypes) {
var Article = sequelize.define("Article", {
id: {type: DataTypes.BIGINT, autoincrement:true, primaryKey: true},
slug: {type: DataTypes.STRING, comment: "Unique URL slug to access the article"},
title: {type: DataTypes.STRING},
content: {type: DataTypes.TEXT},
created: {type: DataTypes.DATE, defaultValue: DataTypes.NOW},
published: {type: DataTypes.BOOLEAN, allowNull: false, defaultValue: true}
}, {
freezeTableName: true,
classMethods: {
associate: function (models) {
Article.belongsTo(models.User, {as: "Author", foreignKey: "author_id"});
Article.hasMany(models.Comment, {as: "Comments", constraints: false});
}
}
});
return Article;
};
and Comment:
module.exports = function(sequelize, DataTypes) {
var Comment = sequelize.define("Comment", {
id: {type: DataTypes.BIGINT, autoincrement:true, primaryKey: true},
content: {type: DataTypes.TEXT},
status: {type: DataTypes.INTEGER, defaultValue: 1},
author: {type: DataTypes.BIGINT},
article: {type: DataTypes.BIGINT}
}, {
freezeTableName: true,
classMethods: {
associate: function (models) {
Comment.hasOne(Comment, {as : "Parent", foreignKey: "parent_id"});
Comment.belongsTo(models.User, {as: "Author", foreignKey: "author_id"});
Comment.belongsTo(models.Article, {as: "Article", foreignKey: "article_id"});
}
}
});
return Comment;
};
The tables are created correctly but I end up with 2 foreign keys each time, for instance this is the Article table in MySQL:
'id','bigint(20)','NO','PRI','0',''
'slug','varchar(255)','YES','',NULL,''
'title','varchar(255)','YES','',NULL,''
'content','text','YES','',NULL,''
'created','datetime','YES','',NULL,''
'published','tinyint(1)','NO','','1',''
'createdAt','datetime','NO','',NULL,''
'updatedAt','datetime','NO','',NULL,''
'author_id','bigint(20)','YES','MUL',NULL,''
'UserId','bigint(20)','YES','',NULL,''
UserId == author_id
User Table:
'id','bigint(20)','NO','PRI','0',''
'firstName','varchar(255)','YES','',NULL,''
'lastName','varchar(255)','YES','',NULL,''
'nickName','varchar(255)','YES','',NULL,''
'email','varchar(255)','YES','UNI',NULL,''
'password','varchar(255)','YES','',NULL,''
'salt','varchar(255)','YES','',NULL,''
'enabled','tinyint(1)','NO','','1',''
'createdAt','datetime','NO','',NULL,''
'updatedAt','datetime','NO','',NULL,''
This table is correct (no foreign keys)
Comment:
'id','bigint(20)','NO','PRI','0',''
'content','text','YES','',NULL,''
'status','int(11)','YES','','1',''
'author','bigint(20)','YES','',NULL,''
'article','bigint(20)','YES','',NULL,''
'createdAt','datetime','NO','',NULL,''
'updatedAt','datetime','NO','',NULL,''
'ArticleId','bigint(20)','YES','',NULL,''
'parent_id','bigint(20)','YES','MUL',NULL,''
'author_id','bigint(20)','YES','MUL',NULL,''
'article_id','bigint(20)','YES','MUL',NULL,''
'UserId','bigint(20)','YES','',NULL,''
ArticleId == article_id and UserId == author_id
As you can see I have the version camel cased and the one I've specified. What did I miss?
** EDIT **
There is no constraints in the database for the camel case field: UserId and ArticleId but Sequelize created the fields in the tables.

You need to add the foreign key on both sides of the relation, e.g:
User.hasMany(models.Article, {constraints: false, foreignKey: 'author_id'});

Related

Sequelize. Specifying the onUpdate parameter does not work when defining an association

I have 2 models:
const Item = sequelize.define('items', {
id: {type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true},
itemPosition: {type: DataTypes.INTEGER, allowNull: false},
title: {type: DataTypes.STRING, unique: true, allowNull: false}
}, {
timestamps: false
})
const ItemRow = sequelize.define('itemRows', {
id: {type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true},
rowPosition: {type: DataTypes.INTEGER, allowNull: false},
serialNumber: {type: DataTypes.INTEGER},
softwareName: {type: DataTypes.STRING, allowNull: false},
linkText: {type: DataTypes.STRING},
link: {type: DataTypes.TEXT},
softwareVersion: {type: DataTypes.STRING}
}, {
timestamps: false
})
Next, I create a one-to-many relationship:
Item.hasMany(ItemRow, {
foreignKey: {
name: 'itemId',
allowNull: false
},
onUpdate: 'CASCADE',
onDelete: 'CASCADE',
hooks: true
})
ItemRow.belongsTo(Item)
Result:
Update Rule: No Action
Deletion Rule: Cascade
Result in Microsoft SQL Server Management Studio
I tried to repeat the example from the documentation on setting up a foreign key with onUpdate and onDelete parameters. Same problem

How to set foreign keys in sequelize

I have an issue creating a foreign keys in my models.I have used sequelize-cli to create models and migrations.Below is my code..
I need to make a relationship between End_user and End_user_authorization model.But i can not see the relation in Dbeaver.What i am doing wrong.Kindly guide.
model/End_user
static associate(models) {
// define association here
End_user.hasOne(models.End_user_authorization,{
foreignKey : 'userId',
as:'End_user_authorization',
onDelete:'CASCADE'
});
}
};
End_user.init({
firstName: DataTypes.STRING,
middleName: DataTypes.STRING,
lastName: DataTypes.STRING,
nickName: DataTypes.STRING,
email: DataTypes.STRING,
mobileNumber: DataTypes.INTEGER,
bloodGroup: DataTypes.STRING,
fatherName: DataTypes.STRING,
motherName: DataTypes.STRING,
fingerPrints: DataTypes.BOOLEAN,
bForm: DataTypes.INTEGER
}, {
sequelize,
modelName: 'End_user',
});
return End_user;
};
model/End_user_authorization
static associate(models) {
// define association here
End_user_authorization.belongsTo(models.End_user,{
foreignKey : 'userId',
onDelete : 'CASCADE'
});
}
};
End_user_authorization.init({
userId: DataTypes.INTEGER,
authCode: DataTypes.STRING,
regDeviceId: DataTypes.INTEGER,
regDeviceName: DataTypes.STRING,
expiry: DataTypes.INTEGER,
ipAddress: DataTypes.STRING
},
{
sequelize,
modelName: 'End_user_authorization',
});
return End_user_authorization;
};
migration/End_user_authorization
Ignore the ipAddress part in migration file
'use strict';
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable('End_user_authorizations', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
userId: {
type: Sequelize.INTEGER,
onDelete: 'CASCADE',
references: {
model: 'End_user',
key: 'id',
}
},
authCode: {
type: Sequelize.STRING
},
regDeviceId: {
type: Sequelize.INTEGER
},
regDeviceName: {
type: Sequelize.STRING
},
expiry: {
type: Sequelize.INTEGER
},
ipAddress: {
type: Sequelize.STRING,
onDelete:'CASCADE',
references:{
model:'AllowedIp',
key:'ipAddress',
}
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: async (queryInterface, Sequelize) => {
await queryInterface.dropTable('End_user_authorizations');
}
};
I have solved the issue.I am sharing becauseit may be helpful to anyone else.Just make sure you create a model first whose Pk will be used to make a relationship with other table....Lets say if you have two models users and posts.
You want to keep User_id in posts model.Make sure to create user model first.

Sequelize Query returning strange models

When I make a query using sequelize, such as this
let User = require('../models').User;
...
User.findOne({ where:{ email : email }}).then( user => {
console.log(user);
})
The user object returned there looks very strange and has some negative side effects. It looks like this (linking because too long):
https://pastebin.com/HLVHVetN
So I can't do things like user.email or user.instanceMethod as many examples show. Even worse, when I attempt to call an instance method, this references the function, not the model.
What am I doing wrong?
EDIT: Here is how my User model looks
'use strict';
let Sequelize = require('sequelize');
let bcrypt = require('bcrypt');
module.exports = (sequelize, DataTypes) => {
let User = sequelize.define('User', {
email: {
type: Sequelize.STRING(100), allowNull: true, unique: true,
validate: {
isEmail: {
msg: 'No es una dirección de correo electrónico.'
},
}
},
google_id: {type: Sequelize.STRING(100)},
facebook_id: {type: Sequelize.STRING(100)},
password_digest: {
type: Sequelize.STRING,
},
password: {
type: Sequelize.VIRTUAL,
allowNull: true,
validate: {
len: [6, Infinity]
}
},
password_confirmation: {
type: Sequelize.VIRTUAL
},
token_reset: {type: Sequelize.STRING(100)},
token_confirm: {type: Sequelize.STRING(100)},
browser_key: {type: Sequelize.STRING(100)},
registry_ip: {type: Sequelize.STRING(60)},
registry_date: {type: Sequelize.DATE, defaultValue: Sequelize.NOW},
account_confirmed: {type: Sequelize.BOOLEAN, defaultValue: false},
terms_accepted: {type: Sequelize.BOOLEAN, allowNull: false},
account_active: {type: Sequelize.BOOLEAN, defaultValue: true},
current_ip: {type: Sequelize.STRING(60)},
login_date: {type: Sequelize.DATE, defaultValue: Sequelize.NOW}
}, {
freezeTableName: true,
indexes: [{unique: true, fields: ['email']}],
instanceMethods: {
authenticate: function (value) {
console.log("THIS",this);
console.log("comparing passwords",value, this.password_digest);
if (bcrypt.compareSync(value, this.password_digest))
return this;
else
return false;
}
}
});`
Inside sequelize promise you will get properties from dataValues. so if you want to access in same thenable promise use
User.dataValues.email
and definately this will point to sequelize object
you need to return the values and use in other function if you don't want to use that promise.
other way around is to use raw:true
User.findOne({raw:true, where:{ email : email }}).then( user => {
console.log(user);
})

Sequelize Querying Many to Many Relationship and Accessing Object

I have a case where I am querying information from two tables that have a many-to-many relationship with a "through" table. When I make my query it appears that I am querying correctly by not using the "through" table as the table join reference and receiving the outputted records with both table attributes, but I am unable to access the field properties of the joined table. Here is the outputted values.
{"fullNameSlug":"Tester Test","email":"test#test.com","firstName":"Tester","lastName":"Test","teams":[{"teamName":"Sales","member":{"memberId":1,"memberEmail":"test#test.com","organizationId":1,"teamId":1,"userId":1,"created_at":"2016-08-21T21:15:19.000Z","updated_at":"2016-08-21T22:00:32.000Z","organization_id":1,"team_id":1,"user_id":1}}]}
Here is my query and how I am setting the data:
.get(function(req, res){
models.User.find({
where: {
organizationId: organization.organizationId
}, attributes: ['email', 'firstName', 'lastName'],
include: [{
model: models.Team,
attributes: ['teamName']
}]
});
}).then(function(currentUsers){
res.jsonp(currentUsers);
console.log(currentUsers);
});
Here is how I was trying to access the teamName in my view: {{currentUsers.teams.teamName}}, which is not returning a value, but {{currentUsers.email}} returns the right user email.
User Table:
module.exports = function(sequelize, DataTypes) {
var User = sequelize.define('user', {
userId: {
type: DataTypes.INTEGER,
field:'user_id',
autoIncrement: true,
primaryKey: true
},
firstName: {
type: DataTypes.STRING,
field: 'first_name'
},
lastName: {
type: DataTypes.STRING,
field: 'last_name'
},
email: {
type: DataTypes.STRING,
isEmail: true,
unique: true,
set: function(val) {
this.setDataValue('email', val.toLowerCase());
}
},
password: DataTypes.STRING,
organizationId: {
type: DataTypes.INTEGER,
field: 'organization_id',
allowNull: true
}
}, {
underscored: true,
freezeTableName: true,
},
classMethods: {
associate: function(db) {
User.belongsToMany(db.Organization, { through: 'member', foreignKey: 'user_id'}),
User.belongsToMany(db.Team, { through: 'member', foreignKey: 'user_id'})
}
});
return User;
}
Team table:
module.exports = function(sequelize, DataTypes) {
var Team = sequelize.define('team', {
teamId: {
type: DataTypes.INTEGER,
field: 'team_id',
autoIncrement: true,
primaryKey: true,
notNull: true
},
teamName: {
type: DataTypes.STRING,
field: 'team_name'
},
organizationId: {
type: DataTypes.INTEGER,
field: 'organization_id'
},
},{
underscored: true,
freezeTableName: true,
classMethods: {
associate: function(db) {
Team.belongsToMany(db.User, { through: 'member', foreignKey: 'team_id' });
},
}
});
return Team;
}
Member Table:
module.exports = function(sequelize, DataTypes) {
var Member = sequelize.define('member', {
memberId: {
type: DataTypes.INTEGER,
field: 'member_id',
autoIncrement: true,
primaryKey: true
},
memberEmail: {
type: DataTypes.STRING,
field: 'member_email',
isEmail: true,
unique: true
},
organizationId: {
type: DataTypes.INTEGER,
field: 'organization_id',
allowNull: true
},
teamId: {
type: DataTypes.INTEGER,
field: 'team_id',
allowNull: true
},
userId: {
type: DataTypes.INTEGER,
field: 'user_id',
allowNull: true
}
},{
underscored: true,
freezeTableName: true,
});
return Member;
}
Outputted SQL:
SELECT `user`.*, `teams`.`team_id` AS `teams.teamId`, `teams`.`team_name` AS `teams.teamName`, `teams.member`.`member_id` AS `teams.member.memberId`, `teams.member`.`member_email` AS `teams.member.memberEmail`, `teams.member`.`organization_id` AS `teams.member.organizationId`, `teams.member`.`team_id` AS `teams.member.teamId`, `teams.member`.`user_id` AS `teams.member.userId`, `teams.member`.`created_at` AS `teams.member.created_at`, `teams.member`.`updated_at` AS `teams.member.updated_at`, `teams.member`.`organization_id` AS `teams.member.organization_id`, `teams.member`.`team_id` AS `teams.member.team_id`, `teams.member`.`user_id` AS `teams.member.user_id` FROM (SELECT `user`.`user_id` AS `userId`, `user`.`email`, `user`.`first_name` AS `firstName`, `user`.`last_name` AS `lastName` FROM `user` AS `user` WHERE `user`.`organization_id` = 1 LIMIT 1) AS `user` LEFT OUTER JOIN (`member` AS `teams.member` INNER JOIN `team` AS `teams` ON `teams`.`team_id` = `teams.member`.`team_id`) ON `user`.`userId` = `teams.member`.`user_id`;
Consider your relations, User has many Teams trough table Member and your query returns user with many teams(array of team objects) as expected. You should use user.teams[0].teamName to get specific team by key, or loop objects in this array

How to force 1:n association with Sequelizejs

I'm implementing some tests to make sure my sequelize objects are saved correctly.
I have a very simple schema: Article <--> Users
An Article is published by ONE User
A User can publish MANY Articles
Here is my Article model definition:
module.exports = function(sequelize){
"use strict";
var Sequelize = require('sequelize');
...
var Article = sequelize.define("Article", {
slug: {
type: Sequelize.STRING,
unique: true,
comment: "Unique URL slug to access the article"
},
title: {
type: Sequelize.STRING,
unique: true,
allowNull: false,
validate: {
notEmpty: true
}
},
summary: {
type: Sequelize.TEXT,
allowNull: true
},
body: {
type: Sequelize.TEXT,
allowNull: true
},
published: {type: Sequelize.BOOLEAN, allowNull: false, defaultValue: true},
allowComment: {type: Sequelize.BOOLEAN, allowNull: false, defaultValue: true}
}, {
freezeTableName: true,
classMethods: {
associate: function (models)
{
Article.belongsTo(models.User, {as: "Author", foreignKey: 'author_id'});
Article.hasMany(models.Comment, {as: "Comments", foreignKey: 'article_id'});
},
articlesForIndex: function()
{
return this.findAll({
where: {published: true},
order: 'createdAt DESC',
limit: 10
});
}
},
setterMethods : {
title : function(v) {
this.setDataValue('title', v.toString());
this.setDataValue('slug', slugify(v));
}
}
});
return Article;
};
What I want to do is forcing the Article to have an Author (User). With the current definition I can create Article without Author.
Here is my test that is failing:
module.exports = function (sequelize, models) {
'use strict';
var Q = require('q');
var should = require('chai').should();
describe('Article Model', function () {
describe('Validation', function () {
...
it('should not be valid without an author', function (done) {
models.Article.create({title: 'title5', summary: 'summary', body: 'body'})
.should.be.rejected.notify(done);
});
});
});
};
On fairly recent (2.0-rc2 i believe) versions of sequelize you can change the foreign key to an object:
Article.belongsTo(User, {
as: "Author",
onDelete: 'CASCADE',
foreignKey: { name:'author_id', allowNull: false }
});
You also need to add onDelete: 'CASCADE' because we can no longer set null on delete

Resources