sequelize.js中的动态默认范围

时间:2016-02-14 13:28:56

标签: node.js sequelize.js

我正在使用sequelize.js构建多种类型的多租户。从技术上讲,我需要按预定义列和当前上下文的动态值过滤所有查询。一般的想法是使用defaultScope来过滤掉其他上下文,例如:

var context = () => { return "some current context id"; }

connection.define('kid', {
  firstName: Sequelize.STRING,
  photoUrl: Sequelize.STRING,
  context: {
    type: Sequelize.STRING,
    defaultValue: context // this part works, it accepts function
  }
}, {
  defaultScope: {
    where: {
      context: context // this does not work, it does not accept function and values is defined only once
    }
  }
});

但是这不起作用,因为在应用程序启动时定义了defaultScope

这样做的正确方法是什么?

3 个答案:

答案 0 :(得分:2)

我不确定它会有所帮助,但您可以随时覆盖模型默认范围。

let defaultScope = {
  where: {
    context: ""
  }
};
defaultScope.where.context = context();
model.addScope('defaultScope',defaultScope,{override: true});

答案 1 :(得分:1)

问题是Sequelize范围是在模型上定义的,但您需要在查询之前应用范围,因为那时您有上下文,例如用户和角色。

以下是Sequelize中范围合并功能的略微修改副本,您可以在beforeFind()

等钩子中使用
// Feel free to write a more fp version; mutations stink.
const {assign, assignWith} = require('lodash')
const applyScope = ({scope, options}) => {
    if (!scope) {
        throw new Error('Invalid scope.')
    }

    if (!options) {
        throw new Error('Invalid options.')
    }

    assignWith(options, scope, (objectValue, sourceValue, key) => {
        if (key === 'where') {
            if (Array.isArray(sourceValue)) {
                return sourceValue
            }
            return assign(objectValue || {}, sourceValue)
        }
        else if (['attributes', 'include'].indexOf(key) >= 0
            && Array.isArray(objectValue)
            && Array.isArray(sourceValue)
        ) {
            return objectValue.concat(sourceValue)
        }

        return objectValue ? objectValue : sourceValue
    })
}

在你的模特中:

 {
    hooks: {
        beforeFind(options) {
            // Mutates options...
            applyScope({
                scope: this.options.scopes.user(options.user)
                , options
            })
            return options
        }
    }
    , scopes: {
        user(user) {
            // Set the scope based on user/role.
            return {
                where: {
                    id: user.id
                }
            }
        }
    }
 }

最后在您的查询中,使用您需要的上下文设置一个选项。

const user = {id: 12, role: 'admin'}
YourModel.findOne({
    attributes: [
        'id'
    ]
    , where: {
        status: 'enabled'
    }
    , user
})

答案 2 :(得分:0)

这里可能为时已晚,但如果定义为函数,作用域可以接受参数。如果范围定义为

,则来自文档 Sequelize scope docs
scopes: {
    accessLevel (value) {
      return {
        where: {
          accessLevel: {
            [Op.gte]: value
          }
        }
      }
    }
    sequelize,
    modelName: 'project'
  }

您可以像这样使用它:Project.scope({ method: ['accessLevel', 19]}).findAll(); 其中 19 是范围将使用的动态值。 根据 defaultScope 我不确定它是否可以定义为函数