NodeJS的sequelize作用域

240 阅读2分钟

这是我参与2022首次更文挑战的第10天,活动详情查看:2022首次更文挑战

作用域

作用域用于帮助复用用代码. 可以定义常用查询,并指定参数如 whereincludelimit 等.

定义

作用域在模型定义中定义,可以是查找器对象,也可以是返回查找器对象的函数 - 默认作用域除外, 该作用域只能是一个对象:

class Project extends Model {}
Project.init({
  // 属性
}, {
  defaultScope: {
    where: {
      active: true
    }
  },
  scopes: {
    deleted: {
      where: {
        deleted: true
      }
    },
    activeUsers: {
      include: [
        { model: User, where: { active: true } }
      ]
    },
    random() {
      return {
        where: {
          someNumber: Math.random()
        }
      }
    },
    accessLevel(value) {
      return {
        where: {
          accessLevel: {
            [Op.gte]: value
          }
        }
      }
    }
    sequelize,
    modelName: 'project'
  }
});

可以在定义模型后通过调用 YourModel.addScope添加作用域,其中在定义另一个模型时可能未定义包含中的模型.

始终应用默认作用域, 使用上面的模型定义,Project.findAll() 将创建以下查询:

SELECT * FROM projects WHERE active = true

可以通过调用 .unscoped().scope(null), 或调用另一个作用域来删除默认作用域:

await Project.scope('deleted').findAll(); // 删除默认作用域
SELECT * FROM projects WHERE deleted = true

也可以在作用域定义中包括作用域模型. 这样可以避免重复 includeattributes 或 where 定义. 使用上面的示例,并在包含的用户模型上调用 active 作用域(而不是直接在该包含对象中指定条件):

// 上例中定义的 `activeUsers` 作用域也可以通过以下方式定义:
Project.addScope('activeUsers', {
  include: [
    { model: User.scope('active') }
  ]
});

使用

通过在模型定义上调用 .scope,并传递一个或多个作用域的名称来应用作用域..scope 返回具有所有常规方法的功能齐全的模型实例:.findAll.update.count.destroy 等, 模型实例并复用

const DeletedProjects = Project.scope('deleted');
await DeletedProjects.findAll();

// 以上相当于:
await Project.findAll({
  where: {
    deleted: true
  }
});

作用域适用于 .find.findAll.count.update.increment 和 .destroy

作用域可以通过两种方式调用, 如果作用域不带任何参数,则可以正常调用它, 如果作用域接受参数,则传递一个对象:

await Project.scope('random', { method: ['accessLevel', 19] }).findAll();

// 生成SQL
SELECT * FROM projects WHERE someNumber = 42 AND accessLevel >= 19

合并

通过将作用域数组传递给 .scope 或将作用域作为连续参数传递,可以同时应用多个作用域.

// 这两个是等效的
await Project.scope('deleted', 'activeUsers').findAll();
await Project.scope(['deleted', 'activeUsers']).findAll();

如果要在默认作用域之外应用另一个作用域,将关键字 defaultScope 传递给.scope:=

await Project.scope('defaultScope', 'deleted').findAll();

调用多个合并作用域时,后续合并作用域中的键将覆盖先前合并作用域中的键(类似于 Object.assign ,除了将合并的 where 和 include 之外. 考虑两个作用域:

YourMode.addScope('scope1', {
  where: {
    firstName: 'bob',
    age: {
      [Op.gt]: 20
    }
  },
  limit: 2
});
YourMode.addScope('scope2', {
  where: {
    age: {
      [Op.gt]: 30
    }
  },
  limit: 10
});