how to have 1:m association in Sequelize? - node.js

I have two models as following:
const User = sequlize.define("user", {...})
const Project = sequelize.define("project". {...})
And I want a 1:M relation between them such that a Project can have multiple users but a User can have a single project.
I tried the following code:
User.hasMany(Project, { as: "project" })
but hasMany adds foreign key to Project table instead of User table. I know its possible to do it the other way around i.e:
Project.hasMany(User, { as: "project" })
but if I do that then when I use the following code it throws no association error.
User.findOne({ where: { id: "username", password: "password"},
include: [{ model: Project, as: 'project', required: true }]
})

Related

getting the follwerList from the through table (sequelize)

Currently making a sns project.
Have a user model and made a N:M association which tells you who is following who.
So there is a connected models between 'user' and 'user'.
This is how my code looks like
static associate(db) {
db.User.hasMany(db.Post);
db.User.belongsToMany(db.User, {
foreignKey: 'followingId',
as: 'Followers',
through: 'Follow',
});
db.User.belongsToMany(db.User, {
foreignKey: 'followerId',
as: 'Followings',
through: 'Follow',
});
}
and I'm trying to show how many followers and following the user has at the profile page.
So what I did is when the /main pass
const User = require('../models/user');
router.use((req, res, next) => {
res.locals.followingList = User.findAll({
where : {followerId : req.user}
});
next();
});
Having a problem accessing the data from through table.
Having a problem accessing the data from through table.
If we want to get the user along with followings and followers then you need to use findOne (or findByPk if req.user is a primary key value) because we want a single user and just include both associations in the query though I don't recommend to include more than one M:N associations to the same query:
res.locals.followingList = await User.findOne({
where : {id : req.user},
include: [{
model: User,
as: 'Followers'
}, {
model: User,
as: 'Followings'
}]
});

Problem with Sequelize and specifying foreign key in an association

I have placements that belong to an App. The foreign_key on the placements table is appId (not AppId). I have specified as follows but get the error:
Unhandled rejection SequelizeDatabaseError: column Placements.AppId does not exist
My Placement Model:
'use strict';
const App = require('./').App
module.exports = (sequelize, DataTypes) => {
const Placement = sequelize.define('Placement', {
name: DataTypes.STRING,
isActive: DataTypes.BOOLEAN,
}, {
tableName: 'placements'
});
Placement.associate = function(models) {
Placement.belongsTo(models.App, { foreignKey: 'appId'})
// associations can be defined here
};
return Placement;
};
How do I tell Sequelize that the foreignKey is appId and not AppId?
Hmm... I don't see nothing wrong with your code. Which version of Sequelize are you using?
Just a couple of non-related comments:
You don't need to import App, it is already in models object.
I think you don't need tableName parameter either.
Try to use the field property
Placement.belongsTo(models.App, {
foreignKey: {
name: 'AppId',
field: 'appId'
}
}
)
You need to have the AppId columns present in App model. Explicitly passing { foreignKey: 'appId'}) to belongsTo makes Sequelize use the key as it is.
So you need to create an App model which has -
const App = sequelize.define('App', {
AppId: DataTypes.INTEGER,
..
}, {
tableName: 'App'
});
Another options would be to change this
Placement.belongsTo(models.App, { foreignKey: 'appId'})
to
Placement.belongsTo(models.App)
This way Sequelize automatically handles all relations between the models.

How to query in sequelize by an association without loading the associated object?

I have multiple models which are associated to each other.
e.g:
var User = sequelize.define("user")
var Project = sequelize.define("project")
Project.hasMany(User)
Now I want to query all Projects containing a specific user.
e.g.:
Project.findAll({
include: [
{
model: User,
where: { id }
}
]
})
This works, but loads also the user and attaches it to the project.
How can I tell sequelize, that the user should not be added to the found projects?
I just managed to address the same problem (using Sequelize 4).
You can specify that you don't want any fields of User just using attributes: [], so your code would become:
Project.findAll({
include: [
{
attributes: [],
model: User,
where: { id }
}
]
})
Many-to-many relationships are defined using the belongsToMany() method in sequelize on both sides. For your specific use case you would have to use a through model for the relation and query the through model directly.
var User = sequelize.define("user")
var Project = sequelize.define("project")
var ProjectUser = sequelize.define("projectUser")
Project.belongsToMany(User, {
through: ProjectUser
})
User.belongsToMany(Project, {
through ProjectUser
});
ProjectUser.findAll({
where: {
UserId: 'someId'
},
// We only want the project, not the user.
// You might need to do ProjectUser.belongsTo() for both
// models for this to work.
include: [Project]
})
.then(function(results) {
// Here we are getting an array of ProjectUsers, to return all the projects
// we map it to a new array of only projects.
return results.map(function(userProject) {
return userProject.Project;
});
})

Creating instance with an association in Sequelize

Using Sequelize, I've created two models: User and Login.
Users can have more than one Login, but a login must have exactly one user, which means a Login cannot be saved without a User ID.
How do I .create a Login with a User association all in one swoop?
Current Code (Doesn't Work)
// Set up the models
var User = sequelize.define('User', {});
var Login = sequelize.define('Login', {});
Login.belongsTo(User, {
onDelete: 'cascade',
foreignKey: {
field: 'userId',
allowNull: false,
}
});
// Create the instances
var user = User.create().then(function() {
// THIS IS WHERE I WOULD LIKE TO SET THE ASSOCIATION
var login = Login.create({
userId: user.get('id')
});
)};
The above results in SequelizeValidationError: notNull Violation: UserId cannot be null
Assuming you have the right association between users and login,
you can just create a user including a login:
User.create({
name: "name",
Login: {...}
},{
include: Login
})
you can find more information here:
https://sequelize.org/docs/v6/advanced-association-concepts/creating-with-associations/
First of all you need to setup the relations in both ways, like this:
// Set up the models
var User = sequelize.define('User', {});
var Login = sequelize.define('Login', {});
// Set the correct associations
User.hasMany(Login, {})
Login.belongsTo(User, {});
Then, you need to properly get the instances returned by the promises:
// Create the instances
User.create({}).then(function(newUser) {
// now you can use newUser acessors to create the login
return newUser.createLogin({});
).then(function(newLogin){
// newLogin
}).catch(function(error){
// error
});
In your .then, the callback receives the model instance created by the previous call. You need to specify the argument inside the callback function.
var user = User.create().then(function(user) {
// THIS IS WHERE I WOULD LIKE TO SET THE ASSOCIATION
var login = Login.create({
userId: user.get('id')
});
return login
}).then(function(login) {
// all creation are complete. do something.
});
Also something important I would like to point out is your missing var statements! Those are important but not related to this question. See Declaring variables without var keyword
An Update to #Arwed Mett's answer
//Create Association Alias or just setting association alias by using 'as' keyword will also work
Login.User = Login.belongsTo(User);
User.create({
name: "name",
Login: {...}
}, {
include: [{
association: Login.User
}]
});
Refrence link - http://docs.sequelizejs.com/manual/tutorial/associations.html#creating-with-associations
For those like me who were trying to create an instance of a model including another instance, like:
var login1 = await Login.create(...);
var user1 = await User.create({
Login: login1
}, {
include: Login
});
You can't because this method is used to embed an instance (Login) which is not already existing and that will be created at the parent instance (User) creation level.
So, if you want to embed an already existing Login in the newly created User, do instead:
var login1 = await Login.create(...);
var user1 = await User.create({
loginId: login1.get('id')
}, {});
You have association between User an Login with constraint allowNull at false. You must create Login before User or set allowNull at true in model and the table to DB (LoginId Null constraint)
var User = sequelize.define('User', {});
var Login = sequelize.define('Login', {});
Login.belongsTo(User, {
onDelete: 'cascade',
foreignKey: {
field: 'userId',
allowNull: false,
}
});
Solution
Login.create({
username: "username",
User: {...}
},{
include: User
})
I have the same issue recently!
I have a typo mistake with the foreignKey config. Use field instead of name caused the issue.
The change below will fix it.
{
foreignKey: {
name: 'userId',
allowNull: false,
}
}
As an extra you could also nest your creation to be even more effective and concise.
// Set up the models
var User = sequelize.define('User', {});
var Login = sequelize.define('Login', {});
...
User.create({
name: "name",
Login:
{
users: {..i.e several users if a user belongs to another user..}
}
},{
include:{
model: Login,
include: User //nested model.Create
}
})
as seen here: https://github.com/sequelize/sequelize/issues/7252

What is populate in SailsJs?

I recently began to develop on sailsJs and not understanding the subtleties
Please explain to me what is populate in SailsJs and who can please do simple example
Thanks in advance ?
whats is ?
User.find({ name: 'foo' })
.populate('pets', { name: 'fluffy' })
.exec(function(err, users) {
if(err) return res.serverError(err);
res.json(users);
});
populate is used for associations. When your model is something like this:
// User.js
module.exports = {
attributes: {
name: {
type: "string"
},
pet: {
model: "pet"
}
}
}
Here pet attribute of user collection is a reference to pet table. In user table it will store only the id column of pet. However, when you do a populate while find, then it will fetch the entire record of the pet entry and display it here. This is just for one to one association. You can have many to one associations as well as many to many. See this documentation for more details
sailsjs project is use the wateline ORM. you can see the document. if you want to use 'populate()', you need define Associations in the model.
.populate()
populate is used with associations to include any related values specified in a model definition. If a collection attribute is defined in a many-to-many, one-to-many or many-to-many-through association the populate option also accepts a full criteria object. This allows you to filter associations and run limit and skip on the results.
as you example, you need do like this:
User.js
// A user may have many pets
var User = Waterline.Collection.extend({
identity: 'user',
connection: 'local-postgresql',
attributes: {
firstName: 'string',
lastName: 'string',
// Add a reference to Pets
pets: {
collection: 'pet',
via: 'owner'
}
}
});
Pet.js
// A pet may only belong to a single user
var Pet = Waterline.Collection.extend({
identity: 'pet',
connection: 'local-postgresql',
attributes: {
breed: 'string',
type: 'string',
name: 'string',
// Add a reference to User
owner: {
model: 'user'
}
}
});
you can ready the doc, and you can use it very easy
To resume what is .populate() (used by waterline) is a little what join is used by SQL.
.populate() allows you to join the tables in your database.
The link identifier is to be defined in your model.
In other words, to associate a user (who is in the "User" table) with a dog (who is in the "Dog" table), you use populate.
To resume your example:
You are looking for the user => User.find ({name: my_user})
You are looking for the dog named "fluffy" => {name: 'fluffy'}
You are looking for the dog 'fluffy' which is associated with your user (belongs) => populate ('pets')
Which give:
User.find({ name: 'foo' })
.populate('pets', { name: 'fluffy' })
.exec(function(err, users) {
if(err) return res.serverError(err);
res.json(users);
}
This association ("pets"), you define it in your models "User" and "Pets" like the example above.
Populate is all about displaying the content of the (id) on which it was refreed
"abc": [{
type: ObjectId,
ref: "xyz"
}],

Resources