Sequelizer connecting multiple hasMany associations - node.js

I am following the offical Sequelizer docs, but I am unable to get it working with multiple "hasMany" associations.
The below code renders an error:
Unhandled rejection Error: [object Object] is not associated to User!
How is this done correctly?
models/user.js
module.exports = function(sequelize, DataTypes) {
var User = sequelize.define("User", {
username: DataTypes.STRING
}, {
classMethods: {
associate: function(models) {
User.hasMany(models.Movie, models.Book)
}
}
});
return User;
};
routes/index.js
var models = require('../models');
var express = require('express');
var router = express.Router();
router.get('/', function(req, res) {
models.User.findAll({
include: [ models.Task, models.Movie ]
}).then(function(users) {
res.render('index', {
title: 'Sequelize: Express Example',
users: users
});
});
});
module.exports = router;

Found the issue, you have to define them individually (eg User.hasMany twice)
module.exports = function(sequelize, DataTypes) {
var User = sequelize.define("User", {
username: DataTypes.STRING
}, {
classMethods: {
associate: function(models) {
User.hasMany(models.Movie),
User.hasMany(models.Books)
}
}
});
return User;
};

Related

Sequelize error: "Association with alias "sender" does not exist on GlobalMessage"

I'm building a Node/Express app with Sequelize. I created some models and made some database migrations with sequelize-cli with minimal difficulty and decided to add some associations between my entities by adding them to the model files and calling sequelize.sync(). Everything seemed to go smoothly until I made a query to test the associations and I got this error: Association with alias "sender" does not exist on GlobalMessage. Below is the related code:
models/globalmessage.js:
'use strict';
module.exports = (sequelize, DataTypes) => {
const GlobalMessage = sequelize.define('GlobalMessage', {
body: DataTypes.TEXT,
timestamp: DataTypes.DATE,
SenderId: {
type: DataTypes.INTEGER,
references: {
model: "Users",
key: "id"
}
}
}, {});
GlobalMessage.associate = function(models) {
// associations can be defined here
GlobalMessage.belongsTo(models.User, {foreignKey: 'SenderId', as: 'sender'});
};
return GlobalMessage;
};
models/user.js:
'use strict';
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define('User', {
email: DataTypes.STRING,
password: DataTypes.STRING,
username: DataTypes.STRING
}, {});
User.associate = function(models) {
// associations can be defined here
User.hasMany(models.GlobalMessage, {
as: 'globalMessages'
})
};
return User;
};
routes/api/user.js:
const Sequelize = require("sequelize");
const express = require("express");
const bcrypt = require('bcrypt');
const saltRounds = 10;
const router = express.Router();
const db = require("../../db/index");
const UserModel = require("../../models/user");
const User = UserModel(db, Sequelize);
const GlobalMessageModel = require("../../models/globalmessage")
const GlobalMessage = GlobalMessageModel(db, Sequelize);
router.post("/findOne", function (request, response, next) {
GlobalMessage.findOne({
where: {body: 'Body of message 1'}, include: 'senders'
})
.then((foundUser) => {
response.json(foundUser)
})
.catch((err) => {
console.log("Error while finding user: ", err)
})
})
module.exports = router;
I get the error when I send a request to localhost/api/user/findOne. I've searched all over and have found little documentation about this specific error and any advice I've found hasn't proven to be fruitful to me. Any help would be appreciated!

Cannot associate tables in sequalize (node js + sequalize)

I've got two tables that are structured like this:
FeedPost
id, text, likecount, commentcount
Comment
id, text, lkpfeedpostid
So there's a foreign key from comment to feedpost via lkpfeedpostid. So one FeedPost could have many Comment. I'm trying to set this up in sequalize so that when I do a FeedPost.findAll(), it pulls back the feed posts with all the comments.
Here's how I've set it up:
Model Definitions:
FeedPost:
module.exports = function(sequelize, DataTypes) {
return sequelize.define('FeedPost', {
...
...
}, {
tableName: 'FeedPost',
classMethods: {
associate : function(models) {
FeedPost.hasMany( Comment, { as: 'comments' });
},
},
});
};
Comment:
module.exports = function(sequelize, DataTypes) {
return sequelize.define('Comment', {
...
...
}, {
tableName: 'Comment',
classMethods: {
associate : function(models) {
Comment.belongsTo(FeedPost, { foreignKey: 'lkpfeedpostid'})
},
},
});
};
I'm importing them like:
const FeedPost = sequelize.import('../models/FeedPost');
const Comment = sequelize.import('../models/Comment');
And passing them into an express router like:
app.use(feed(FeedPost, Comment));
module.exports = (FeedPost, Comment) => {
const api = Router();
api.get('/api/feed', (req, res) => {
FeedPost.findAll( { include: [ {model: Comment, as: 'Comment' }]}).then(results => {
res.json({results});
});
}
I've tried tons of variations of this found in these links:
http://docs.sequelizejs.com/manual/tutorial/models-definition.html#import
http://docs.sequelizejs.com/class/lib/associations/has-many.js~HasMany.html
https://github.com/sequelize/sequelize/issues/3273
And whenever I hit the route, I keep getting the error that says
Unhandled rejection SequelizeEagerLoadingError: Comment is not associated to Feed!
I am going with that assumption that your sequelize version >=4.
classMethods and instanceMethods have been removed. The changelog can be found here : Sequelize V4 Breaking Changes
From their docs you can refer the current method to specify associations.
FeedPost
module.exports = function(sequelize, DataTypes) {
const FeedPost = sequelize.define('FeedPost', {/*Attributes here*/});
FeedPost.hasMany(Comment, { as: 'comments' })
};
Comment
module.exports = function(sequelize, DataTypes) {
const Comment = sequelize.define('Comment', {/*Attributes here*/});
Comment.BelongsTo(Comment, { foreignKey: 'lkpfeedpostid', targetKey: 'id' })
};
The following query should work:
FeedPost.findAll( { include: [ {model: Comment, as: 'comments' }]})

A is not associated to B

Looked on the internet about similar questions/errors, none of them helped me...
Unhandled rejection SequelizeEagerLoadingError: Task is not associated to User!
My users route
router.get('', function (req, res) {
models.User.findAll({
include: [
{
model: models.Task,
as: 'tasks'
}
]
}).then(function (users) {
res.send(users);
});
});
User model
'use strict';
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define('User', {
first_name: DataTypes.STRING,
last_name: DataTypes.STRING,
email: DataTypes.STRING
}, {
underscored: true
});
User.associations = function (models) {
User.hasMany(models.Task, { as: 'tasks' });
};
return User;
};
Task model
'use strict';
module.exports = (sequelize, DataTypes) => {
const Task = sequelize.define('Task', {
name: DataTypes.STRING
}, {
underscored: true
});
Task.associations = function (models) {
Task.belongsTo(models.User);
};
return Task;
};
I associate them both, and made a bidirectional relationship..
As you are using the finder function for association with as option, Sequelize cannot find that alias, as it is not defined explicitly anywhere. Please try this one:
User.associations = function (models) {
User.hasMany(models.Task, { as: 'tasks' });
};
Hope this helps.

Sequelize belongstomany associations : create a post and associate its existing tags

I got two models associates by belongsToMany associations.
Posts.js :
'use strict';
module.exports = function(sequelize, DataTypes) {
var Posts = sequelize.define('Posts', {
title: DataTypes.STRING,
body: DataTypes.STRING
}, {
classMethods: {
associate: function(models) {
Posts.belongsToMany(models.Tags, {
through: 'PostsTags',
as:'posts_has_tags',
foreignKey:'postId'
});
}
}
});
return Posts;
};
and Tags.js :
'use strict';
module.exports = function(sequelize, DataTypes) {
var Tags = sequelize.define('Tags', {
name: DataTypes.STRING
},{
classMethods: {
associate: function(models) {
Tags.belongsToMany(models.Posts, {
through: 'PostsTags',
as:'posts_has_tags',
foreignKey:'tagId'
});
}
}
});
return Tags;
};
In my db i got 2 existing tags. (id:1 and id:2)
I got trouble when i create a Post i want to associate it to one of these tags.
By running this code :
create: function(request, reply){
let post = Object.assign({}, request.payload, request.params);
models.Posts.create({
title: post.title,
body: post.body,
posts_has_tags: [{tagId:1}]
},{
include: [{model:models.Tags, as:'posts_has_tags'}]
}).then(function(newPost){
if(!newPost){
return reply('[404] Not found').code(404);
}
reply(newPost).code(200);
})
}
it runs this query :
INSERT INTO `Posts` (`id`,`title`,`body`,`createdAt`,`updatedAt`) VALUES (DEFAULT,'TITLE 1','BODY 1','2017-02-05 18:33:21','2017-02-05 18:33:21');
Executing (default): INSERT INTO `Tags` (`id`,`createdAt`,`updatedAt`) VALUES (DEFAULT,'2017-02-05 18:33:21','2017-02-05 18:33:21');
Executing (default): INSERT INTO `PostsTags` (`createdAt`,`updatedAt`,`postId`,`tagId`) VALUES ('2017-02-05 18:33:21','2017-02-05 18:33:21',70,20);
It creates a new post but also a new Tag that i don't want.
It creates the associations in poststags table but with the bad tag id (the one just created instead of the id 1)
In the promise how can i access to the setAssociation() or addAssocation() method sequelize is supposed to create on belongsToMany associations :
I got errors or undefined if i try newPost.addTags or newPost.setTags, or models.Posts.addTags or models.Posts.setTags
If anybody can help me, he's welcome.
I stuck on this for days and i'd like to understand right how i can use sequelize the best way.
I changed the alias "as" for both models :
Posts.js :
'use strict';
module.exports = function(sequelize, DataTypes) {
var Posts = sequelize.define('Posts', {
title: DataTypes.STRING,
body: DataTypes.STRING
}, {
classMethods: {
associate: function(models) {
Posts.belongsToMany(models.Tags, {
through: 'PostsTags',
as:'postHasTags',
foreignKey:'postId'
});
}
}
});
return Posts;
};
Tags.js :
'use strict';
module.exports = function(sequelize, DataTypes) {
var Tags = sequelize.define('Tags', {
name: DataTypes.STRING
},{
classMethods: {
associate: function(models) {
Tags.belongsToMany(models.Posts, {
through: 'PostsTags',
as:'tagHasPosts',
foreignKey:'tagId'
});
}
}
});
return Tags;
};
Both alias are reversed.
When i create a new post or update one, i can access to the setter built by sequelize (model.setAssociation) in the promise :
upsert: function(request, reply){
let receivedPost = Object.assign({}, request.payload, request.params);
models.Posts.find({
where:{ id:receivedPost.id }
}).then(post => {
if(post){
post.setPostHasTags(receivedPost.tags)
reply(post).code(200)
} else {
models.Posts.create({
title: receivedPost.title,
body: receivedPost.body
}).then(newPost => {
newPost.setPostHasTags(receivedPost.tags)
reply(newPost).code(200);
});
}
});

Using Sequelize with associations and scopes with includes in multiple files

I'm using this way to keep my Sequelize models in separate files and everything works pretty well but now I came up with the idea to have scopes with includes in it.
Something like this doesn't work:
var User = sequelize.define("User", {...}, {
scopes: {
complete: {
include: [{
model: Task
}]
}
}
});
... Since Task is (of course) not defined. Even using require('.').Task instead doesn't help at this point because User gets loaded before Task and by the time User is loaded, Task is not yet defined.
So, is there a simple and easy way without a dozen workarounds to have
associations
scopes with includes
... All of this in a separate file per model?
and if the model (Task) is not yet loaded? (in this case T comes before U and the model is loaded).
Object.keys(db).forEach(function(modelName) {
if ("associate" in db[modelName]) {
db[modelName].associate(db);
}
});
becomes
Object.keys(db).forEach(function(modelName) {
if ("associate" in db[modelName]) {
db[modelName].associate(db);
}
});
Object.keys(db).forEach(function(modelName) {
if ("loadScopes" in db[modelName]) {
db[modelName].loadScopes(db);
}
});
and the model
module.exports = function(sequelize, DataTypes) {
var User = sequelize.define("User", {
username: DataTypes.STRING
}, {
classMethods: {
associate: function(models) {
User.hasMany(models.Task)
}
}
});
return User;
};
becomes
module.exports = function(sequelize, DataTypes) {
var User = sequelize.define("User", {
username: DataTypes.STRING
}, {
classMethods: {
associate: function(models) {
User.hasMany(models.Task)
}
loadScopes: function(models) {
User.addScope('complete', {
include: [{
model: models.Task
}]
})
}
}
});
return User;
};
Using the addScope() method is better for include models in scope. This is because Sequelize models are defined alphabetically, Model A comes first before Model B. When you include Model B in Model A scope, by the time Model A runs Model B is undefined. addScope() method solves this issue.
module.exports = function(sequelize, DataTypes) {
const User = sequelize.define('User', {
username: DataTypes.STRING
}, { });
User.associate = function(models) {
// associations can be defined here
User.hasMany(models.Task);
// scopes should be defined here
User.addScope('defaultScope', {
include: [{ model: models.Task }],
});
// scopes with parameter
User.addScope('byProfile', profileId => ({
where: { profileId },
}));
};
return User;
};
using default scope:
User.findAll();
This will use the default scope by default.
using byProfile scope:
User.scope({ method: ['byProfile', profile.id] }).findAll();
Try to use Sequelize.models.Task
module.export = function(Sequelize, DataTypes){
var User = sequelize.define("User", {...}, {
scopes: {
complete: {
include: [{
model: Sequelize.models.Task
}]
}
}
});
}
Or use addScope() to add them without having to worry about order of loading each model

Resources