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
andcreate
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 :
this.Post.hasMany(this.Comment, {
foreignKey: 'commentable_id',
scope: {
commentable: 'post'
}
});
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
:
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:
class Post extends Model {}
Post.init(attributes, {
defaultScope: {
where: {
active: true
}
},
scopes: {
deleted: {
where: {
deleted: true
}
}
},
sequelize,
});
User.hasMany(Post); // regular getPosts association
User.hasMany(Post.scope('deleted'), { as: 'deletedPosts' });
User.getPosts(); // WHERE active = true
User.getDeletedPosts(); // WHERE deleted = true