7. Scopes 作用域的使用

优质
小牛编辑
130浏览
2023-12-01

Scopes - 作用域,表示一个限制范围,它最终会生成SQL查询中的where子句。它在模型定义方法sequelize.defineoption参数,或通过Model.scope()方法指定。

  1. 定义
  2. 使用
  3. 合并
  4. 关联

1. 定义

作用域允许你定义常用的查询,这样就可以很容易地在之后使用。Scopes包括所有相同属性规律的筛选器,wherewherelimit等。

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

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

在这个查询中limitagescope2同相同的键值覆盖,

当进行查询时,默认的作用域逻辑会被同时使用:

Project.scope('deleted').findAll({
  where: {
    firstName: 'john'
  }
})
WHERE deleted = true AND firstName = 'john'

4. 关联

在表关联中,Sequelize有两中不同但作用相关的概念。其区别不大,但很重要:

  • 关联作用域 - 允许你获取和设置关联定义默认属性,这在多表关联时很有用。这个作用域仅在使用getsetaddcreate函数时调用
  • 模型上的作用域 - 在匹配关联时,允许你作用默认值和其它作用范围。这些作用域都适用于模型匹配,及关联查找

以下示例中包含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