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.

  1. class Player extends Model {}
  2. Player.init({/* attributes */}, { sequelize, modelName: 'player' });
  3. class Team extends Model {}
  4. Team.init({/* attributes */}, { sequelize, modelName: 'team' });
  5. 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.

  1. class User extends Model {}
  2. User.init({/* attributes */}, { sequelize, modelName: 'user' })
  3. class Company extends Model {}
  4. Company.init({/* attributes */}, { sequelize, modelName: 'company' });
  5. // will add companyId to user
  6. User.belongsTo(Company);
  7. class User extends Model {}
  8. User.init({/* attributes */}, { underscored: true, sequelize, modelName: 'user' })
  9. class Company extends Model {}
  10. Company.init({
  11. uuid: {
  12. type: Sequelize.UUID,
  13. primaryKey: true
  14. }
  15. }, { sequelize, modelName: 'company' });
  16. // will add companyUuid to user with field company_uuid
  17. User.belongsTo(Company);

In cases where as has been defined it will be used in place of the target model name.

  1. class User extends Model {}
  2. User.init({/* attributes */}, { sequelize, modelName: 'user' })
  3. class UserRole extends Model {}
  4. UserRole.init({/* attributes */}, { sequelize, modelName: 'userRole' });
  5. 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:

  1. class User extends Model {}
  2. User.init({/* attributes */}, { sequelize, modelName: 'user' })
  3. class Company extends Model {}
  4. Company.init({/* attributes */}, { sequelize, modelName: 'company' });
  5. 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.

  1. class User extends Model {}
  2. User.init({/* attributes */}, { sequelize, modelName: 'user' })
  3. class Company extends Model {}
  4. Company.init({/* attributes */}, { sequelize, modelName: 'company' });
  5. 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.

  1. class User extends Model {}
  2. User.init({/* ... */}, { sequelize, modelName: 'user' })
  3. class Project extends Model {}
  4. Project.init({/* ... */}, { sequelize, modelName: 'project' })
  5. // One-way associations
  6. Project.hasOne(User)
  7. /*
  8. In this example hasOne will add an attribute projectId to the User model!
  9. Furthermore, Project.prototype will gain the methods getUser and setUser according
  10. to the first parameter passed to define. If you have underscore style
  11. enabled, the added attribute will be project_id instead of projectId.
  12. The foreign key will be placed on the users table.
  13. You can also define the foreign key, e.g. if you already have an existing
  14. database and want to work on it:
  15. */
  16. Project.hasOne(User, { foreignKey: 'initiator_id' })
  17. /*
  18. Because Sequelize will use the model's name (first parameter of define) for
  19. the accessor methods, it is also possible to pass a special option to hasOne:
  20. */
  21. Project.hasOne(User, { as: 'Initiator' })
  22. // Now you will get Project.getInitiator and Project.setInitiator
  23. // Or let's define some self references
  24. class Person extends Model {}
  25. Person.init({ /* ... */}, { sequelize, modelName: 'person' })
  26. Person.hasOne(Person, {as: 'Father'})
  27. // this will add the attribute FatherId to Person
  28. // also possible:
  29. Person.hasOne(Person, {as: 'Father', foreignKey: 'DadId'})
  30. // this will add the attribute DadId to Person
  31. // In both cases you will be able to do:
  32. Person.setFather
  33. Person.getFather
  34. // If you need to join a table twice you can double join the same table
  35. Team.hasOne(Game, {as: 'HomeTeam', foreignKey : 'homeTeamId'});
  36. Team.hasOne(Game, {as: 'AwayTeam', foreignKey : 'awayTeamId'});
  37. 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.

  1. class User extends Model {}
  2. User.init({/* attributes */}, { sequelize, modelName: 'user' })
  3. class Company extends Model {}
  4. Company.init({/* attributes */}, { sequelize, modelName: 'company' });
  5. // Adds companyName attribute to User
  6. // Use name attribute from Company as source attribute
  7. 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.

  1. class Player extends Model {}
  2. Player.init({/* attributes */}, { sequelize, modelName: 'player' })
  3. class Team extends Model {}
  4. 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

  1. Player.belongsTo(Team);
  2. //Or
  3. Player.hasOne(Team);

Having Team as the source and Player as the target

  1. Team.belongsTo(Player);
  2. //Or
  3. 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.

  1. class Player extends Model {}
  2. Player.init({/* attributes */}, { sequelize, modelName: 'player' })
  3. class Coach extends Model {}
  4. Coach.init({/* attributes */}, { sequelize, modelName: 'coach' })
  5. class Team extends Model {}
  6. 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.

  1. 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.

  1. Coach.hasOne(Team) // `coachId` will be added on Team / Target model