sequelize association explanation on docs - node.js

while reading sequelize doc association part, explanation below really confuses me
hasOne and belongsTo insert the association key in different models
from each other. hasOne inserts the association key in target model
whereas belongsTo inserts the association key in the source model.
and also,
Player.belongsTo(Team); // Will add a teamId attribute to Player to hold the primary key value for Team
it seems that if I set relation between two models, sequelize automatically adds foreign key to the target model.
but according to the article,
https://lorenstewart.me/2016/09/12/sequelize-table-associations-joins/z
we have to manually add foreign key to the model file.
which one is right..?
it has been a while since I questioned about this issue.
Any detailed explanation would really be appreciated.
Any recommended articles about sequelize association to read would also be appreciated since sequelize doc seems not that kind for ORM beginners.

Actually it depends on how are you creating your tables. Via sync or via migrations.
For example
const User = sequelize.define('user', {
username: Sequelize.STRING,
});
const Address = sequelize.define('add', {
address: Sequelize.STRING,
});
User.hasOne(Address);
sequelize.sync({ force: true })
.then(() => User.create({
username: 'test'
}))
.then(item => {
console.log(item.dataValues.itemPrice);
});
This will create a userId field in the database
Note that I am use sync. However if you are using migrations, you would have to create this on your own.
PS: Its better to use migrations instead of sync.

Related

Sequelize: Defining Associations

Reading the documentation of Sequelize I'm in some level confused, what Sequelize will provide automatically for us and what we need to explicitly tell it.
I have two models: User and Post. As you have guessed a User can have multiple Posts and a Post belongs only to one User. Setting the respective relationships will look so:
Post.associate = (models) => {
Post.belongsTo(models.users, {
as:'user',
foreignKey: {
name: 'user_id',
allowNull: false
}
}
}
User.associate = (models) => {
User.hasMany(models.posts, {
as:'posts',
onDelete:'CASCADE',
onUpdate:'CASCADE'
}
}
My question is: should I specify the foreignKey one more time when declaring the hasMany association, or it is enough for Sequelize to have the foreignKey in one of the declared relationships between two models (in the example - belongsTo)?
From what I think happens:
Sequelize goes through all your association one by one
If you already provided a foreign key name then fine
Else it will guess/name the foreign key on its own
Like what it says about options.foreignKey in docs e.g. for belongsTo : https://sequelize.org/master/class/lib/model.js~Model.html#static-method-belongsTo (same description for hasOne, hasMany, belongsToMany )
options.foreignKey || string OR object || optional
The name of the foreign key attribute in the source table or an object representing the type definition for the foreign column (see Sequelize.define for syntax). When using an object, you can add a name property to set the name of the column. Defaults to the name of target + primary key of target
If sequelize is guessing your foreignKey names then you will face issues only if your foreignKey name is not matching (tableName + Id) OR (tableName + _ + id)
💡 Hence, better to give foreignKey names on your own to both sides of associations to never face any issues going further.

referencing a model that hasn't been created, avoid the error: relation "TABLENAME" does not exist

I am very new to sequelize and postgresql. I looked a lot for an answer to this issue but I couldn't find anything related to my it. If this question is has an answer in another post I apologize for repeating and I'll gladly refer to the other post and remove my post from here.
At the beginning I did:
sequelize model:generate --name user --attributes username:string,email:string,password:string,collection_id:INTEGER
where collection_id references another table ID. I did the same for the collection table:
sequelize model:generate --name collection --attributes plant_id:integer,user_id:integer
in my models I updated the association
in the collection model I added:
models.collection.belongsTo(models.user, {foreignKey: 'user_id'})
and in the user model I added:
models.user.hasOne(models.collection, {foreignKey: 'collection_id'})
And in the migration files I added:
user_id: {
type: Sequelize.INTEGER,
references: {
model: {
tableName: 'user'
}
}
}
collection_id: {
type: Sequelize.INTEGER,
references: {
model: {
tableName: 'collection'
}
}
}
in their respective files. Now the issue here is that sequelize is trying to migrate the user model before the collection model, however the later is referenced in the first model and I am getting this error "relation "collection" does not exist" which only makes sense. Now if I removed the reference, is there a way to add it later after I migrate?
reference sets a foreign key on the table under the hood. But you can't have 2 tables with foreign keys pointing to each other (circular reference).
You set a foreign key on the "child" table which points to the "parent" table. In your case, if collection belongs to user then collection is "child".
You can read this paragraph from Sequalize docs with some details about the difference between hasOne and belongsTo (and read about those associations in Sequalize separately as well). {foreignKey: 'collection_id'} which you pass to hasOne method is actually supposed to be in collections table (target model), means in your case it doesn't make sense and should be like this instead:
models.user.hasOne(models.collection, {foreignKey: 'user_id'})
So, you don't need collection_id column in your user table, can safely remove it.

How to define many to many with Feathers and Sequelize with additional fields in join table?

I am struggling to find the solution for that.
I want to have users which can belong to many organizations.
Each user can have a different role (I would prefer even roles but it sounds even more complicated...) at a specific organization.
In the table like User_Organization_Role I need to have fields like role (roleId?), isActive. Maybe some more.
I am using Feathers Plus generator but I do not think it matters in this case, however it may be beneficial to add something to the schema file?
I thought having belongsTo with simple organizationId field will be sufficient but I've realized that changing that to manyToMany, later on, would be painful so I think it is much better to implement that now.
I will appreciate any solutions / suggestions / best practices etc.
n:m relations are by far the most difficult to handle, and there's really no one-size-fits-all solution. The biggest thing is to read and understand this page and its sub-pages, and then read them 2 more times for good measure. Try to focus on doing one thing at a time. I outline how I would approach this with feathersjs in this issue:
https://github.com/feathersjs/feathers/issues/852#issuecomment-406413342
The same technique could be applied in any application... the basic flow goes like this:
Create or update your primary objects first (users, organizations, roles, etc.). There are no relations made at this point. You need to have your objects created before you can make any relations.
Create or update the relations. This involves updating a "join" table (aka: "mapping" or "through" table) with data from step #1. The join table can (and should) have its own model. It should contain a foreign key for each of the objects you are associating (userId, organizationId, roleId etc.). You can put other fields in this table too.
Here is some pseudo code for how I would define my models (only showing relevant code for brevity). There is a little more to it than what I describe below, but this should get you started.
const UserOrganizationRole = sequelize.define('User_Organization_Role', {
// Define any custom fields you want
foo: DataTypes.STRING
})
// Let sequelize add the foreign key fields for you.
// Also, save a reference to the relationship - we will use it later
User.Organization = User.belongsToMany(Organization, { through: UserOrganizationRole });
User.Role = User.belongsToMany(Role, { through: UserOrganizationRole });
Organization.User = Organization.belongsToMany(User, { through: UserOrganizationRole });
Role.User = Role.belongsToMany(User, { through: UserOrganizationRole });
... and here is how I would go about handling inserts
const user = await User.create({ ... });
const org = await Organization.create({ ... });
const role = await Role.create({ ... });
await UserOrganizationRole.create({
userId: user.id,
organizationId: org.id,
roleId: role.id,
foo: 'bar'
});
... and finally, load the data like so:
// Now we can reference those relationships we created earlier:
const user = await User.findById(123, {
include: [User.Organization, User.Role]
});
const org = await Organization.findById(456, {
include: [Organization.User]
});

How to insert foreign key in sequelize model

I have a User model and a Houses model in sequelize. I also have a UserHouse model that contains {user_id, house_id}. How do I populate values in the UserHouse model.
I went through the sequelize documentation but could not get a clear idea.
I need to know, how to insert user_id and house_id in UserHouse model.
I also cannot understand what associations should I use between these models.
Any help will be appreciated.
Edited:
One More issue:
I have one more model HouseInfo that stores house_id (foreign key),
how should I store this "house_id" foreign key in db and how should I associate the models.
User.belongsToMany(House, { through: UserHouse });
House.belongsToMany(User, { through: UserHouse });
user.addHouse(house);
house.setUsers([user1, user2])
http://docs.sequelizejs.com/en/latest/docs/associations/#belongs-to-many-associations
http://docs.sequelizejs.com/en/latest/api/associations/belongs-to-many/

How to implemet a Many-to-Many relationship in Sequelizejs which also have relationship attributes?

So there are a lot of answers that explain how you model a Many-to-Many relationship in sequelizejs using hasMany() etc. But none of them has explained how and where do you store attributes which are created due to such an association, for eg: A customer can belong to or have many merchants and a merchant can have many customers, one such attribute of this relationship is a unique customer_id for a particular merchant-cutomer. Now where should this key(and any other detail) reside if we follow this: Stackoverflow answer
If you want additional attributes in your join table, you can define a model for the join table in sequelize, before you define the association, and then tell sequelize that it should use that model for joining, instead of creating a new one:
Customer = sequelize.define('customer', {})
Merchant = sequelize.define('merchant', {})
MerchantCustomers = sequelize.define('merchant_customers', {
customer_id: DataTypes.INTEGER
})
Merchant.belongsToMany(Customer, { through: MerchantCustomers })
Customer.belongsToMany(Merchant, { through: MerchantCustomers })
customer.addMerchant(merchant, { customer_id: 42 })
http://docs.sequelizejs.com/en/latest/docs/associations/#belongs-to-many-associations
To access the join table attributes:
c.getMerchants().then(function (merchants) {
merchants[0].merchant_customer.customer_id // Or perhaps merchant_customers, can't remember
});

Resources