Difference between HasOne and BelongsTo in Sequelize ORM

node.jsOrmsequelize.js

node.js Problem Overview


I am developing a sails.js app with sequelize ORM. I am a little confused as to when BelongsTo and HasOne need to be used.

The documentation states that :

> BelongsTo associations are associations where the foreign key for the > one-to-one relation exists on the source model. > > HasOne associations are associations where the foreign key for the > one-to-one relation exists on the target model.

Is there any other difference apart from the the place where these are specified? Does the behavior still continue to be the same in either cases?

node.js Solutions


Solution 1 - node.js

This is more universal problem.

The main difference is in semantic. you have to decide what is the relationship (Some silly example):

Man has only one right arm. Right arm belongs to one man.

Saying it inversely looks a little weird:

Right arm has a man. A man belongs to right arm.

You can have man without right arm. But alone right arm is useless.

In sequelize if RightArm and Man are models, it may looks like:

Man.hasOne(RightArm);      // ManId in RigthArm
RightArm.belongsTo(Man);   // ManId in RigthArm

And as you notice there is also difference in db table structure:

BelongsTo will add the foreignKey on the source where hasOne will add on the target (Sequelize creates new column 'ManId' in table 'RightArm' , but doesn't create 'RightArmId' column in 'Man' table).

I don't see any more differences.

Solution 2 - node.js

I agree with Krzysztof Sztompka about the difference between:

Man.hasOne(RightArm);
RightArm.belongsTo(Man);

I'd like to answer Yangjun Wang's question:

> So in this case, should I use either Man.hasOne(RightArm); or > RightArm.belongsTo(Man);? Or use them both?

It is true that the Man.hasOne(RightArm); relation and the RightArm.belongsTo(Man); one do the same thing - each of these relations will add the foreign key manId to the RightArm table.

From the perspective of the physical database layer, these methods do the same thing, and it makes no difference for our database which exact method we will use.

So, what's the difference? The main difference lays on the ORM's layer (in our case it is Sequalize ORM, but the logic below applies to Laravel's Eloquent ORM or even to Ruby's Active Record ORM).

Using the Man.hasOne(RightArm); relation, we will be able to populate the man's RightArm using the Man model. If this is enough for our application, we can stop with it and do not add the RightArm.belongsTo(Man); relation to the RightArm model.

But what if we need to get the RightArm's owner? We won't be able to do this using the RightArm model without defining the RightArm.belongsTo(Man); relation on the RightArm model.

One more example will be the User and the Phone models. Defining the User.hasOne(Phone) relation, we will be able to populate our User's Phone. Without defining the Phone.belongsTo(User) relation, we won't be able to populate our Phone's owner (e.g. our User). If we define the Phone.belongsTo(User) relation, we will be able to get our Phone's owner.

So, here we have the main difference: if we want to be able to populate data from both models, we need to define the relations (hasOne and belongsTo) on both of them. If it is enough for us to get only, for example, User's Phone, but not Phone's User, we can define only User.hasOne(Phone) relation on the User model.

The logic above applies to all the ORMs that have hasOne and belongsTo relations.

I hope this clarifies your understanding.

Solution 3 - node.js

I know this is a 4-years late answer, but I've been thinking of it, searching the docs, and googling since yesterday. And couldn't find an answer that convinced me about what was happening. Today I've got to a conclusion: the difference is not just a matter of semantics, definitely!

Let's suppose you have the following statement (from the docs):

Project.hasMany(Task);

It creates, in Project model, some utility methods on the instances of Project, like: addTask, setTask etc. So you could do something like:

const project = await Project.create({...});

// Here, addTask exists in project instance as a 
// consequence of Project.hasMany(Task); statement 
project.addTasks([task1, task2]);

Also, in the database, a foreign key in tasks relation would've been created, pointing to projects relation.

Now if, instead of Project.hasMany(Task);, I had stated only:

Task.belongsTo(Project);

Then, similarly, in the database, foreign keys in tasks relation would've been created, pointing to projects relation. But there wouldn't be any addTasks method on project instances though. But, by doing Task.belongsTo(Project);, Sequelize would create a different set of methods, but only on task instances this time. After doing that, you could associate a task to a project using, for example:

const proj = await Project.findByPk(...);
const task1 = await Task.create({...});

...

// Here, setProject exists in task instance as a 
// consequence of Task.belongsTo(Project); statement 
task1.setProject(proj);

The docs defines as source, the model that owns the method used to create the association. So, in:

  • Project.hasMany(Task);: In this statement, Project is the source model. Task is, in turn, the target model.
  • Task.belongsTo(Project);: In this statement, Task is the source model. Project is, in turn, the target model.

The thing is that, when creating associations using hasOne, hasMany, belongsTo, and belongsToMany, the instances utility methods are created only on the source model. In summary: if you want to have the utility methods created both in Project and Task instances, you must use the two statements for describing the same the association. In the database itself, both will have the same redundant effect (creating a foreign key on tasks relation pointing to projects relation's primary key):

// All the instances of Project model will have utility methods
Project.hasMany(Task);

// All the instances of Task model will have utility methods
Task.belongsTo(Project);

const project = await Project.create(...);
const task1 = await Task.create(...);
const task2 = await Task.create(...);

...

// as a consequence of Project.hasMany(Task), this can be done:
project.addTask(task1);

...

// as a consequence of Task.belongsTo(Project), this can be done:
task2.setProject(project);

BTW, after writing this answer, I realized that this is the same thing that Vladsyslav Turak is explaining in his answer, but I decided to keep my answer here because it adds some important practical information involving the utility methods stuff.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJaseem AbbasView Question on Stackoverflow
Solution 1 - node.jsKrzysztof SztompkaView Answer on Stackoverflow
Solution 2 - node.jsVladyslav TurakView Answer on Stackoverflow
Solution 3 - node.jsjulianobrasilView Answer on Stackoverflow