7. Scopes 作用域的使用
Scopes
- 作用域,表示一个限制范围,它最终会生成SQL查询中的where
子句。它在模型定义方法sequelize.define
的option
参数,或通过Model.scope()
方法指定。
- 定义
- 使用
- 合并
- 关联
1. 定义
作用域允许你定义常用的查询,这样就可以很容易地在之后使用。Scopes
包括所有相同属性规律的筛选器,where
、where
、limit
等。
Scopes
在定义模型时定义,可以是筛选对象,或是返回筛选对象的函数-除默认限制范围外,它只能是一个对象:
var Project = sequelize.define('project', { // Attributes }, { defaultScope: { where: { active: true } }, scopes: { deleted: { where: { deleted: true } }, activeUsers: { include: [ { model: User, where: { active: true }} ] } random: function () { return { where: { someNumber: Math.random() } } }, accessLevel: function (value) { return { where: { accessLevel: { $gte: value } } } } } });
也可以模型定义后通过addScope
方法添加作用域。这对在使用include
包含限制时尤其适用,因为在模型的include
在定义时包括的模型可能还未定义。
默的作用总是会被使用,也就是说,在上面定义的模型中使用Project.findAll()
查询时,会创建如下查询语句:
SELECT * FROM projects WHERE active = true
默认的作用域可以使用.unscoped()
、.scope(null)
、或引入一个其它的作用域移除:
Project.scope('deleted').findAll(); // 移除默认作用域
SELECT * FROM projects WHERE deleted = true
2. 使用
作用域通过调用.scope
方法应用到模型定义的范围内。该方法会传入一个或多个范围的名称,并范围返回一个全功能的模型实例的所有规则的方法,如:.findAll
、.update
、.count
、destroy
:
var DeletedProjects = Project.scope('deleted'); DeletedProjects.findAll(); // some time passes // let's look for deleted projects again! DeletedProjects.findAll();
作用域被引入时有两种方式:如果不传入参数则正常引用,如果传入参数那么以对象的形式传入:
Project.scope('random', { method: ['accessLevel', 19]}).findAll();
SELECT * FROM projects WHERE someNumber = 42 AND accessLevel >= 19
3. 合并
作用域可以同时应用多个,使用用时只要向.scope
方法传入数组参数或做为连续的参数传入即可:
// 两种等价的方式 Project.scope('deleted', 'activeUsers').findAll(); Project.scope(['deleted', 'activeUsers']).findAll();
如果想在另一个范围内使用默认作用域,通过defaultScope
参数传入.scope
即可:
Project.scope('defaultScope', 'deleted').findAll();
SELECT * FROM projects WHERE active = true AND deleted = true
通过对象引入多重作用域时,当key值相同,后引入的作用域会覆盖前面的作用域规则:
{ scope1: { where: { firstName: 'bob', age: { $gt: 20 } }, limit: 2 }, scope2: { where: { age: { $gt: 30 } }, limit: 10 }
WHERE firstName = 'bob' AND age > 30 LIMIT 10
在这个查询中limit
和age
被scope2
同相同的键值覆盖,
当进行查询时,默认的作用域逻辑会被同时使用:
Project.scope('deleted').findAll({ where: { firstName: 'john' } })
WHERE deleted = true AND firstName = 'john'
4. 关联
在表关联中,Sequelize有两中不同但作用相关的概念。其区别不大,但很重要:
- 关联作用域 - 允许你获取和设置关联定义默认属性,这在多表关联时很有用。这个作用域仅在使用
get
、set
、add
、create
函数时调用 - 模型上的作用域 - 在匹配关联时,允许你作用默认值和其它作用范围。这些作用域都适用于模型匹配,及关联查找
以下示例中包含Post 和 Comment两个模型,Comment又关联到一些其它的模型,可以理解为通过commentable_id
列引用一个commentable
表:
foreignKey: 'commentable_id', scope: { commentable: 'post' } });
当调用post.getComments()
方法时,会自动生成WHERE commentable = 'post'
语句。类似的,当发表一个新的评论Comment
会自动设置为'post'
。
我们还应该考虑,Post 模型应该只向用户显示状态为活跃的数据:where: { active: true }
。这个活跃状态会同样应用于所关联的模型,而不是像commentable
那样没有关联范围。这样,使用默认作用域直接调用Post.findAll()
方法即可,同样可用于User.getPosts()
方法。
禁用默认作用域,在访问器中传入scope: null
即可,如:User.getPosts({ scope: null })
,如下同样:
User.getPosts({ scope: ['scope1', 'scope2']});
如果你想创建一个关联作用域的快捷方法,那么可以在模型关联中定义。如,创建用户所有已删除的Post的作用域:
var Post = sequelize.define('post', attributes, { defaultScope: { where: { active: true } }, scopes: { deleted: { where: { deleted: true } } } }); User.hasMany(Post); // regular getPosts association User.hasMany(Post.scope('deleted'), { as: 'deletedPosts' });
User.getPosts(); // WHERE active = true User.getDeletedPosts(); // WHERE deleted = true