One-To-One associations
One-To-One associations are associations between exactly two models connected by a single foreign key.
BelongsTo
BelongsTo associations are associations where the foreign key for the one-to-one relation exists on the source model.
A simple example would be a Player being part of a Team with the foreign key on the player.
class Player extends Model {}
Player.init({/* attributes */}, { sequelize, modelName: 'player' });
class Team extends Model {}
Team.init({/* attributes */}, { sequelize, modelName: 'team' });
Player.belongsTo(Team); // Will add a teamId attribute to Player to hold the primary key value for Team
Foreign keys
By default the foreign key for a belongsTo relation will be generated from the target model name and the target primary key name.
The default casing is camelCase
. If the source model is configured with underscored: true
the foreignKey will be created with field snake_case
.
class User extends Model {}
User.init({/* attributes */}, { sequelize, modelName: 'user' })
class Company extends Model {}
Company.init({/* attributes */}, { sequelize, modelName: 'company' });
// will add companyId to user
User.belongsTo(Company);
class User extends Model {}
User.init({/* attributes */}, { underscored: true, sequelize, modelName: 'user' })
class Company extends Model {}
Company.init({
uuid: {
type: Sequelize.UUID,
primaryKey: true
}
}, { sequelize, modelName: 'company' });
// will add companyUuid to user with field company_uuid
User.belongsTo(Company);
In cases where as
has been defined it will be used in place of the target model name.
class User extends Model {}
User.init({/* attributes */}, { sequelize, modelName: 'user' })
class UserRole extends Model {}
UserRole.init({/* attributes */}, { sequelize, modelName: 'userRole' });
User.belongsTo(UserRole, {as: 'role'}); // Adds roleId to user rather than userRoleId
In all cases the default foreign key can be overwritten with the foreignKey
option.When the foreign key option is used, Sequelize will use it as-is:
class User extends Model {}
User.init({/* attributes */}, { sequelize, modelName: 'user' })
class Company extends Model {}
Company.init({/* attributes */}, { sequelize, modelName: 'company' });
User.belongsTo(Company, {foreignKey: 'fk_company'}); // Adds fk_company to User
Target keys
The target key is the column on the target model that the foreign key column on the source model points to. By default the target key for a belongsTo relation will be the target model's primary key. To define a custom column, use the targetKey
option.
class User extends Model {}
User.init({/* attributes */}, { sequelize, modelName: 'user' })
class Company extends Model {}
Company.init({/* attributes */}, { sequelize, modelName: 'company' });
User.belongsTo(Company, {foreignKey: 'fk_companyname', targetKey: 'name'}); // Adds fk_companyname to User
HasOne
HasOne associations are associations where the foreign key for the one-to-one relation exists on the target model.
class User extends Model {}
User.init({/* ... */}, { sequelize, modelName: 'user' })
class Project extends Model {}
Project.init({/* ... */}, { sequelize, modelName: 'project' })
// One-way associations
Project.hasOne(User)
/*
In this example hasOne will add an attribute projectId to the User model!
Furthermore, Project.prototype will gain the methods getUser and setUser according
to the first parameter passed to define. If you have underscore style
enabled, the added attribute will be project_id instead of projectId.
The foreign key will be placed on the users table.
You can also define the foreign key, e.g. if you already have an existing
database and want to work on it:
*/
Project.hasOne(User, { foreignKey: 'initiator_id' })
/*
Because Sequelize will use the model's name (first parameter of define) for
the accessor methods, it is also possible to pass a special option to hasOne:
*/
Project.hasOne(User, { as: 'Initiator' })
// Now you will get Project.getInitiator and Project.setInitiator
// Or let's define some self references
class Person extends Model {}
Person.init({ /* ... */}, { sequelize, modelName: 'person' })
Person.hasOne(Person, {as: 'Father'})
// this will add the attribute FatherId to Person
// also possible:
Person.hasOne(Person, {as: 'Father', foreignKey: 'DadId'})
// this will add the attribute DadId to Person
// In both cases you will be able to do:
Person.setFather
Person.getFather
// If you need to join a table twice you can double join the same table
Team.hasOne(Game, {as: 'HomeTeam', foreignKey : 'homeTeamId'});
Team.hasOne(Game, {as: 'AwayTeam', foreignKey : 'awayTeamId'});
Game.belongsTo(Team);
Even though it is called a HasOne association, for most 1:1 relations you usually want the BelongsTo association since BelongsTo will add the foreignKey on the source where hasOne will add on the target.
Source keys
The source key is the attribute on the source model that the foreign key attribute on the target model points to. By default the source key for a hasOne
relation will be the source model's primary attribute. To use a custom attribute, use the sourceKey
option.
class User extends Model {}
User.init({/* attributes */}, { sequelize, modelName: 'user' })
class Company extends Model {}
Company.init({/* attributes */}, { sequelize, modelName: 'company' });
// Adds companyName attribute to User
// Use name attribute from Company as source attribute
Company.hasOne(User, {foreignKey: 'companyName', sourceKey: 'name'});
Difference between HasOne and BelongsTo
In Sequelize 1:1 relationship can be set using HasOne and BelongsTo. They are suitable for different scenarios. Lets study this difference using an example.
Suppose we have two tables to link Player and Team. Lets define their models.
class Player extends Model {}
Player.init({/* attributes */}, { sequelize, modelName: 'player' })
class Team extends Model {}
Team.init({/* attributes */}, { sequelize, modelName: 'team' });
When we link two models in Sequelize we can refer them as pairs of source and target models. Like this
Having Player as the source and Team as the target
Player.belongsTo(Team);
//Or
Player.hasOne(Team);
Having Team as the source and Player as the target
Team.belongsTo(Player);
//Or
Team.hasOne(Player);
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.
Here is an example demonstrating use cases of BelongsTo and HasOne.
class Player extends Model {}
Player.init({/* attributes */}, { sequelize, modelName: 'player' })
class Coach extends Model {}
Coach.init({/* attributes */}, { sequelize, modelName: 'coach' })
class Team extends Model {}
Team.init({/* attributes */}, { sequelize, modelName: 'team' });
Suppose our Player
model has information about its team as teamId
column. Information about each Team's Coach
is stored in the Team
model as coachId
column. These both scenarios requires different kind of 1:1 relation because foreign key relation is present on different models each time.
When information about association is present in source model we can use belongsTo
. In this case Player
is suitable for belongsTo
because it has teamId
column.
Player.belongsTo(Team) // `teamId` will be added on Player / Source model
When information about association is present in target model we can use hasOne
. In this case Coach
is suitable for hasOne
because Team
model store information about its Coach
as coachId
field.
Coach.hasOne(Team) // `coachId` will be added on Team / Target model