Associations

Sequelize has two different but related scope concepts in relation to associations. The difference is subtle but important:

  • Association scopes Allow you to specify default attributes when getting and setting associations - useful when implementing polymorphic associations. This scope is only invoked on the association between the two models, when using the get, set, add and create associated model functions
  • Scopes on associated models Allows you to apply default and other scopes when fetching associations, and allows you to pass a scoped model when creating associations. These scopes both apply to regular finds on the model and to find through the association.

As an example, consider the models Post and Comment. Comment is associated to several other models (Image, Video etc.) and the association between Comment and other models is polymorphic, which means that Comment stores a commentable column, in addition to the foreign key commentable_id.

The polymorphic association can be implemented with an association scope :

  1. this.Post.hasMany(this.Comment, {
  2. foreignKey: 'commentable_id',
  3. scope: {
  4. commentable: 'post'
  5. }
  6. });

When calling post.getComments(), this will automatically add WHERE commentable = 'post'. Similarly, when adding new comments to a post, commentable will automagically be set to 'post'. The association scope is meant to live in the background without the programmer having to worry about it - it cannot be disabled. For a more complete polymorphic example, see Association scopes

Consider then, that Post has a default scope which only shows active posts: where: { active: true }. This scope lives on the associated model (Post), and not on the association like the commentable scope did. Just like the default scope is applied when calling Post.findAll(), it is also applied when calling User.getPosts() - this will only return the active posts for that user.

To disable the default scope, pass scope: null to the getter: User.getPosts({ scope: null }). Similarly, if you want to apply other scopes, pass an array like you would to .scope:

  1. User.getPosts({ scope: ['scope1', 'scope2']});

If you want to create a shortcut method to a scope on an associated model, you can pass the scoped model to the association. Consider a shortcut to get all deleted posts for a user:

  1. class Post extends Model {}
  2. Post.init(attributes, {
  3. defaultScope: {
  4. where: {
  5. active: true
  6. }
  7. },
  8. scopes: {
  9. deleted: {
  10. where: {
  11. deleted: true
  12. }
  13. }
  14. },
  15. sequelize,
  16. });
  17. User.hasMany(Post); // regular getPosts association
  18. User.hasMany(Post.scope('deleted'), { as: 'deletedPosts' });
  1. User.getPosts(); // WHERE active = true
  2. User.getDeletedPosts(); // WHERE deleted = true