前言
在使用Sequelize
的Scopes
的时候,遇到了一些坑,也很好奇里面怎样实现的,所以特意看一遍源码~~
定义
定义方式有两种:
第一种
在Sequelize.define(modelName, attributes, options)
设置options.defaultScope
和options.scopes
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| define(modelName, attributes, options) { options = options || {};
options.modelName = modelName; options.sequelize = this;
const model = class extends Model {};
model.init(attributes, options);
return model; }
|
实际上是从 Model.init(attributes, options)
定义并检查
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| static init(attributes, options) {
...
this.options = Object.assign({ timestamps: true, validate: {}, freezeTableName: false, underscored: false, underscoredAll: false, paranoid: false, rejectOnEmpty: false, whereCollection: null, schema: null, schemaDelimiter: '', defaultScope: {}, scopes: [], indexes: [] }, options);
...
this._scope = this.options.defaultScope; this._scopeNames = ['defaultScope'];
if (_.isPlainObject(this._scope)) { this._conformOptions(this._scope, this); }
_.each(this.options.scopes, scope => { if (_.isPlainObject(scope)) { this._conformOptions(scope, this); } });
...
return this; }
|
第二种
从Model.addScope(name, scope, options)
增加,如果添加的是重复的或者是defaultScope
则需要options.override = true
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| static addScope(name, scope, options) { options = _.assign({ override: false }, options);
if ((name === 'defaultScope' || name in this.options.scopes) && options.override === false) { throw new Error('The scope ' + name + ' already exists. Pass { override: true } as options to silence this error'); }
this._conformOptions(scope, this);
if (name === 'defaultScope') { this.options.defaultScope = this._scope = scope; } else { this.options.scopes[name] = scope; } }
|
使用
scope
通过Model.scope(option)
方法调用,该方法可以传入一个或者多个scope
名称或者方法,并返回一个全功能的Model
,可以调用原来Model
的所有方法,如:Model.findAll(options)
、Model.update(values, options)
、Model.count(options)
、Model.destroy(options)
等等
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
| static scope(option) { const self = class extends this {}; let scope; let scopeName;
Object.defineProperty(self, 'name', { value: this.name }); self._scope = {}; self._scopeNames = []; self.scoped = true; if (!option) { return self; }
const options = _.flatten(arguments);
for (const option of options) { scope = null; scopeName = null;
if (_.isPlainObject(option)) { if (option.method) { if (Array.isArray(option.method) && !!self.options.scopes[option.method[0]]) { scopeName = option.method[0]; scope = self.options.scopes[scopeName].apply(self, option.method.slice(1)); } else if (self.options.scopes[option.method]) { scopeName = option.method; scope = self.options.scopes[scopeName].apply(self); } } else { scope = option; } } else { if (option === 'defaultScope' && _.isPlainObject(self.options.defaultScope)) { scope = self.options.defaultScope; } else { scopeName = option; scope = self.options.scopes[scopeName]; if (_.isFunction(scope)) { scope = scope(); this._conformOptions(scope, self); } } } if (scope) { _.assignWith(self._scope, scope, (objectValue, sourceValue, key) => { if (key === 'where') { return Array.isArray(sourceValue) ? sourceValue : Object.assign(objectValue || {}, sourceValue); } else if (['attributes', 'include', 'group'].indexOf(key) >= 0 && Array.isArray(objectValue) && Array.isArray(sourceValue)) { return objectValue.concat(sourceValue); } return objectValue ? objectValue : sourceValue; });
self._scopeNames.push(scopeName ? scopeName : 'defaultScope'); } else { throw new sequelizeErrors.SequelizeScopeError('Invalid scope ' + scopeName + ' called.'); } }
return self; }
|
值得一提是,Model.scope(option)
参数真值为false
即去除所有scope
,包括defaultScope
,也可以通过调用unscoped()
1 2 3 4
| static unscoped() { return this.scope(); }
|
那么其实设置了scopes
之后,在查询中如何体现呢?
1 2 3 4 5 6 7
| static findAll(options) { ... this._injectScope(options); ... }
|
总结
根据以上源码可以知道,
- 调用
Model.scope(option)
之后的Model
是一个全功能的Model
,只是修改了Model._scope
,之前怎么用现在也可以一样用,包括在scope.include
里面使用
- 我们可以通过两种方法定义
scope
,如果在Sequelize.define(modelName, attributes, options)
不方便定义defaultScope
的时候,可以通过Model.addScope(name, scope, { override: true})
覆盖
- 如果定义
defaultScope
,只能定义Object
scope
合并规则是key
后者覆盖前者
attributes
,include
和group
,不会覆盖,只会concat
连接
where
也不会被覆盖,where
里面的key
会后者覆盖前者
- 如果使用多个
scope
,合并的时候需要注意参数顺序,免得发生预料之外的合并结果
- 如果不需要
scope
,调用Model.scope()
或者Model.unscope()
即可去除