不要怂,就是干,撸起袖子干!

Commit fe5a85b0 by Jan Aagaard Meier

refactor(scopes): Simplified implementation, added more tests, 💄 remove…

…d cruft from query generator
1 parent f970157b
......@@ -17,6 +17,7 @@
#### Backwards compatibility changes
- Events support have been removed so using `.on('succes')` or `.succes()` is no longer supported.
- Trying to apply a scope that does not exist will always throw an error
# 2.0.6
- [BUG] Don't update virtual attributes in Model.update. Fixes [#2860](https://github.com/sequelize/sequelize/issues/2860)
......
......@@ -351,7 +351,16 @@ module.exports = (function() {
});
}
return association.target.findAll(options, queryOptions);
var model = association.target;
if (options.hasOwnProperty('scope')) {
if (!options.scope) {
model = model.unscoped();
} else {
model = model.scope(options.scope);
}
}
return model.findAll(options, queryOptions);
};
obj[this.accessors.hasAll] = function(instances, options) {
......@@ -359,21 +368,24 @@ module.exports = (function() {
, where;
options = options || {};
options.scope = false;
instances.forEach(function(instance) {
if (instance instanceof association.target.Instance) {
where = new Utils.or([where, instance.primaryKeyValues]);
where = { $or: [where, instance.primaryKeyValues]};
} else {
var _where = {};
_where[association.target.primaryKeyAttribute] = instance;
where = new Utils.or([where, _where]);
where = { $or: [where, _where]};
}
});
options.where = new Utils.and([
where,
options.where
]);
options.where = {
$and: [
where,
options.where
]
};
return instance[association.accessors.get](
options,
......@@ -388,6 +400,7 @@ module.exports = (function() {
, where;
options = options || {};
options.scope = false;
if (param instanceof association.target.Instance) {
where = param.primaryKeyValues;
......@@ -396,10 +409,12 @@ module.exports = (function() {
where[association.target.primaryKeyAttribute] = param;
}
options.where = new Utils.and([
where,
options.where
]);
options.where = {
$and: [
where,
options.where
]
};
return instance[association.accessors.get](
options,
......@@ -419,7 +434,9 @@ module.exports = (function() {
options = options || {};
var instance = this;
return instance[association.accessors.get]({}, {
return instance[association.accessors.get]({
scope: false
}, {
transaction: options.transaction,
logging: options.logging
}).then(function(oldAssociatedObjects) {
......@@ -668,6 +685,7 @@ module.exports = (function() {
}
return instance[association.accessors.get]({
scope: false,
where: newInstance.primaryKeyValues
}, {
transaction: (additionalAttributes || {}).transaction,
......
......@@ -97,20 +97,31 @@ module.exports = (function() {
BelongsTo.prototype.injectGetter = function(instancePrototype) {
var association = this;
instancePrototype[this.accessors.get] = function(params) {
instancePrototype[this.accessors.get] = function(options) {
var where = {};
where[association.targetIdentifier] = this.get(association.identifier);
params = association.target.__optClone(params) || {};
params.where = (params.where && [params.where]) || [];
options = association.target.__optClone(options) || {};
where[association.targetIdentifier] = this.get(association.identifier);
params.where.push(where);
options.where = {
$and: [
options.where,
where
]
};
params.where = new Utils.and(params.where);
if (options.limit === undefined) options.limit = null;
if (params.limit === undefined) params.limit = null;
var model = association.target;
if (options.hasOwnProperty('scope')) {
if (!options.scope) {
model = model.unscoped();
} else {
model = model.scope(options.scope);
}
}
return association.target.find(params);
return model.find(options);
};
return this;
......
......@@ -18,16 +18,27 @@ module.exports = (function() {
}.bind(this));
}
options.where = new Utils.and([
new Utils.where(
this.target.rawAttributes[this.association.identifier],
this.instance[this.source.primaryKeyAttribute]
),
scopeWhere,
options.where
]);
return this.association.target.all(options, queryOptions);
options.where = {
$and: [
new Utils.where(
this.target.rawAttributes[this.association.identifier],
this.instance[this.source.primaryKeyAttribute]
),
scopeWhere,
options.where
]
};
var model = this.association.target;
if (options.hasOwnProperty('scope')) {
if (!options.scope) {
model = model.unscoped();
} else {
model = model.scope(options.scope);
}
}
return model.all(options, queryOptions);
};
HasManySingleLinked.prototype.injectSetter = function(oldAssociations, newAssociations, defaultAttributes) {
......@@ -100,7 +111,7 @@ module.exports = (function() {
updateWhere[primaryKey] = unassociatedIds;
promises.push(this.association.target.update(
promises.push(this.association.target.unscoped().update(
update,
Utils._.extend(options, {
allowNull: [self.association.identifier],
......
......@@ -334,6 +334,7 @@ module.exports = (function() {
obj[this.accessors.get] = function(options, queryOptions) {
options = association.target.__optClone(options) || {};
queryOptions = queryOptions || {};
var Class = Object(association.through.model) === association.through.model ? HasManyDoubleLinked : HasManySingleLinked;
return new Class(association, this).injectGetter(options, queryOptions);
......@@ -344,21 +345,24 @@ module.exports = (function() {
, where;
options = options || {};
options.scope = false;
instances.forEach(function(instance) {
if (instance instanceof association.target.Instance) {
where = new Utils.or([where, instance.primaryKeyValues]);
where = { $or: [where, instance.primaryKeyValues]};
} else {
var _where = {};
_where[association.target.primaryKeyAttribute] = instance;
where = new Utils.or([where, _where]);
where = { $or: [where, _where]};
}
});
options.where = new Utils.and([
where,
options.where
]);
options.where = {
$and: [
where,
options.where
]
};
return instance[association.accessors.get](
options,
......@@ -373,6 +377,7 @@ module.exports = (function() {
, where;
options = options || {};
options.scope = false;
if (param instanceof association.target.Instance) {
where = param.primaryKeyValues;
......@@ -381,10 +386,12 @@ module.exports = (function() {
where[association.target.primaryKeyAttribute] = param;
}
options.where = new Utils.and([
where,
options.where
]);
options.where = {
$and: [
where,
options.where
]
};
return instance[association.accessors.get](
options,
......@@ -420,7 +427,9 @@ module.exports = (function() {
var instance = this;
return instance[association.accessors.get]({}, {
return instance[association.accessors.get]({
scope: false
}, {
transaction: (additionalAttributes || {}).transaction,
logging: (additionalAttributes || {}).logging
}).then(function(oldAssociatedObjects) {
......@@ -467,7 +476,8 @@ module.exports = (function() {
}
return instance[association.accessors.get]({
where: newInstance.primaryKeyValues
where: newInstance.primaryKeyValues,
scope: false
}, {
transaction: (additionalAttributes || {}).transaction
}).then(function(currentAssociatedObjects) {
......@@ -483,7 +493,9 @@ module.exports = (function() {
obj[this.accessors.remove] = function(oldAssociatedObject, options) {
var instance = this;
return instance[association.accessors.get]({}, options).then(function(currentAssociatedObjects) {
return instance[association.accessors.get]({
scope: false
}, options).then(function(currentAssociatedObjects) {
var newAssociations = [];
if (!(oldAssociatedObject instanceof association.target.Instance)) {
......@@ -506,7 +518,9 @@ module.exports = (function() {
obj[this.accessors.removeMultiple] = function(oldAssociatedObjects, options) {
var instance = this;
return instance[association.accessors.get]({}, options).then(function(currentAssociatedObjects) {
return instance[association.accessors.get]({
scope: false
}, options).then(function(currentAssociatedObjects) {
var newAssociations = [];
// Ensure the oldAssociatedObjects array is an array of target instances
......
......@@ -96,20 +96,31 @@ module.exports = (function() {
HasOne.prototype.injectGetter = function(instancePrototype) {
var association = this;
instancePrototype[this.accessors.get] = function(params) {
instancePrototype[this.accessors.get] = function(options) {
var where = {};
where[association.identifier] = this.get(association.sourceIdentifier);
params = association.target.__optClone(params) || {};
params.where = (params.where && [params.where]) || [];
options = association.target.__optClone(options) || {};
where[association.identifier] = this.get(association.sourceIdentifier);
params.where.push(where);
options.where = {
$and: [
options.where,
where
]
};
params.where = new Utils.and(params.where);
if (options.limit === undefined) options.limit = null;
if (params.limit === undefined) params.limit = null;
var model = association.target;
if (options.hasOwnProperty('scope')) {
if (!options.scope) {
model = model.unscoped();
} else {
model = model.scope(options.scope);
}
}
return association.target.find(params);
return model.find(options);
};
return this;
......@@ -121,6 +132,8 @@ module.exports = (function() {
instancePrototype[this.accessors.set] = function(associatedInstance, options) {
var instance = this;
options = options || {};
options.scope = false;
return instance[association.accessors.get](options).then(function(oldInstance) {
if (oldInstance) {
oldInstance[association.identifier] = null;
......
......@@ -366,7 +366,7 @@ Mixin.getAssociation = function(target, alias) {
if (this.associations.hasOwnProperty(associationName)) {
var association = this.associations[associationName];
if (association.target === target && (alias === undefined ? !association.isAliased : association.as === alias)) {
if (association.target.name === target.name && (alias === undefined ? !association.isAliased : association.as === alias)) {
return association;
}
}
......
......@@ -1223,7 +1223,7 @@ module.exports = (function() {
} else if (Utils._.isPlainObject(options.where)) {
options.where['__' + as] = subQueryWhere;
} else {
options.where = new Utils.and(options.where, subQueryWhere);
options.where = { $and: [options.where, subQueryWhere] };
}
}
......@@ -1533,7 +1533,9 @@ module.exports = (function() {
result = (value === 'NULL') ? key + ' IS NULL' : [key, value].join(smth.comparator);
} else if (_.isPlainObject(value)) {
result = this.plainObjectToWhere(value, key, key, factory).join(' AND ');
result = this.whereItemQuery(smth.attribute, value, {
model: factory
});
} else {
if (typeof value === 'boolean') {
value = this.booleanValue(value);
......@@ -1726,7 +1728,6 @@ module.exports = (function() {
outerBinding = '';
if (key === '$not') outerBinding = 'NOT ';
if (Array.isArray(value)) {
value = value.map(function (item) {
var itemQuery = self.whereItemsQuery(item, options, ' AND ');
......@@ -2166,83 +2167,6 @@ module.exports = (function() {
return [_key, _value].join(' ' + logicResult + ' ');
},
/*
Takes a hash and transforms it into a mysql where condition: {key: value, key2: value2} ==> key=value AND key2=value2
The values are transformed by the relevant datatype.
*/
hashToWhereConditions: function(hash, dao, options) {
var result = [];
options = options || {};
// Closures are nice
Utils._.each(hash, function(value, key) {
var _key
, _value = null;
if (value && value._isSequelizeMethod === true && (value instanceof Utils.literal)) {
result.push(value.val);
return;
}
if (options.keysEscaped) {
_key = key;
} else {
if (this.isAssociationFilter(key, dao)) {
_key = key = this.getAssociationFilterColumn(key, dao, options);
} else {
_key = this.quoteIdentifiers(key);
}
}
if (Array.isArray(value)) {
result.push(this.arrayValue(value, key, _key, dao, 'IN'));
} else if (value && Utils._.isPlainObject(value)) {
result = result.concat(this.plainObjectToWhere(value, key, _key, dao));
} else {
if (typeof value === 'boolean') {
_value = this.booleanValue(value);
} else {
_value = this.escape(value);
}
result.push((_value === 'NULL') ? _key + ' IS NULL' : [_key, _value].join('='));
}
}.bind(this));
return result.join(' AND ');
},
plainObjectToWhere: function (value, key, _key, dao) {
var _value
, result = [];
if (!!value.join) {
//using as sentinel for join column => value
_value = this.quoteIdentifiers(value.join);
result.push([_key, _value].join('='));
} else {
for (var logic in value) {
var logicResult = Utils.getWhereLogic(logic, value[logic]);
if (logicResult === 'BETWEEN' || logicResult === 'NOT BETWEEN') {
_value = this.escape(value[logic][0]);
var _value2 = this.escape(value[logic][1]);
result.push(' (' + _key + ' ' + logicResult + ' ' + _value + ' AND ' + _value2 + ') ');
} else if (logicResult === 'IN' || logicResult === 'NOT IN' || Array.isArray(value[logic])) {
var values = Array.isArray(value[logic]) ? value[logic] : [value[logic]];
result.push(this.arrayValue(values, key, _key, dao, logicResult));
} else {
_value = this.escape(value[logic]);
result.push([_key, _value].join(' ' + logicResult + ' '));
}
}
}
return result;
},
booleanValue: function(value) {
return value;
}
......
......@@ -481,7 +481,7 @@ module.exports = (function() {
if (!resultMap[itemHash]) {
$parent = resultMap[parentHash];
$lastKeyPrefix = lastKeyPrefix(prevKey);
//console.log($parent, prevKey, $lastKeyPrefix);
if (includeMap[prevKey].association.isSingleAssociation) {
$parent[$lastKeyPrefix] = resultMap[itemHash] = values;
} else {
......@@ -561,7 +561,7 @@ module.exports = (function() {
if (!resultMap[itemHash]) {
$parent = resultMap[parentHash];
$lastKeyPrefix = lastKeyPrefix(prevKey);
//console.log($parent, prevKey, $lastKeyPrefix);
if (includeMap[prevKey].association.isSingleAssociation) {
$parent[$lastKeyPrefix] = resultMap[itemHash] = values;
} else {
......
......@@ -680,22 +680,6 @@ module.exports = (function() {
options.type = QueryTypes.SELECT;
// See if we need to merge options and model.scopeObj
// we're doing this on the QueryInterface level because it's a bridge between
// sequelize and the databases
if (model.options.defaultScope && Object.keys(model.options.defaultScope).length > 0) {
if (!!options) {
Utils.injectScope.call(model, options, true);
}
var scopeObj = buildScope.call(model);
Object.keys(scopeObj).forEach(function(method) {
if (typeof scopeObj[method] === 'number' || !Utils._.isEmpty(scopeObj[method])) {
options[method] = scopeObj[method];
}
});
}
options.instance = model;
return this.sequelize.query(
this.QueryGenerator.selectQuery(tableName, options, model),
......@@ -935,16 +919,5 @@ module.exports = (function() {
return this.sequelize.query(sql, options);
};
// private
var buildScope = function() {
var smart;
// Use smartWhere to convert several {where} objects into a single where object
smart = Utils.smartWhere(this.scopeObj.where || [], this.daoFactoryManager.sequelize.options.dialect);
smart = Utils.compileSmartWhere.call(this, smart, this.daoFactoryManager.sequelize.options.dialect);
return {limit: this.scopeObj.limit || null, offset: this.scopeObj.offset || null, where: smart, order: (this.scopeObj.order || []).join(', ')};
};
return QueryInterface;
})();
......@@ -568,7 +568,8 @@ module.exports = (function() {
delete options.modelName;
var factory = new Model(modelName, attributes, options);
this.modelManager.addDAO(factory.init(this.modelManager));
factory = factory.init(this.modelManager);
this.modelManager.addDAO(factory);
this.runHooks('afterDefine', factory);
......
......@@ -290,7 +290,7 @@ if (Support.dialectIsMySQL()) {
)
};
}],
expectation: "SELECT * FROM `myTable` WHERE (`myTable`.`archived` IS NULL AND COALESCE(`place_type_codename`, `announcement_type_codename`) IN ('Lost','Found'));",
expectation: "SELECT * FROM `myTable` WHERE (`myTable`.`archived` IS NULL AND COALESCE(`place_type_codename`, `announcement_type_codename`) IN ('Lost', 'Found'));",
context: QueryGenerator,
needsSequelize: true
}, {
......@@ -564,38 +564,6 @@ if (Support.dialectIsMySQL()) {
arguments: ['User', ['foo', 'bar']],
expectation: 'DROP INDEX user_foo_bar ON `User`'
}
],
hashToWhereConditions: [
{
arguments: [{ id: [1, 2, 3] }],
expectation: '`id` IN (1,2,3)'
},
{
arguments: [{ id: [] }],
expectation: '`id` IN (NULL)'
},
{
arguments: [{ maple: false, bacon: true }],
expectation: '`maple`=false AND `bacon`=true'
},
{
arguments: [{ beaver: [false, true] }],
expectation: '`beaver` IN (false,true)'
},
{
arguments: [{birthday: new Date(Date.UTC(2011, 6, 1, 10, 1, 55))}],
expectation: "`birthday`='2011-07-01 10:01:55'"
},
{
arguments: [{ birthday: new Date(Date.UTC(2011, 6, 1, 10, 1, 55)),
otherday: new Date(Date.UTC(2013, 6, 2, 10, 1, 22)) }],
expectation: "`birthday`='2011-07-01 10:01:55' AND `otherday`='2013-07-02 10:01:22'"
},
{
arguments: [{ birthday: [new Date(Date.UTC(2011, 6, 1, 10, 1, 55)), new Date(Date.UTC(2013, 6, 2, 10, 1, 22))] }],
expectation: "`birthday` IN ('2011-07-01 10:01:55','2013-07-02 10:01:22')"
}
]
};
......
......@@ -368,8 +368,8 @@ describe(Support.getTestDialectTeaser('Model'), function() {
return self.Environment.find({
where: { name: 'environment' },
include: [
{ daoFactory: self.Domain, as: 'PrivateDomain' },
{ daoFactory: self.Domain, as: 'PublicDomain' }
{ model: self.Domain, as: 'PrivateDomain' },
{ model: self.Domain, as: 'PublicDomain' }
]
}).then(function(environment) {
expect(environment).to.exist;
......@@ -559,7 +559,7 @@ describe(Support.getTestDialectTeaser('Model'), function() {
it('throws an error if alias is not associated', function() {
var self = this;
return self.Worker.find({ include: [{ daoFactory: self.Task, as: 'Work' }] }).catch (function(err) {
return self.Worker.find({ include: [{ model: self.Task, as: 'Work' }] }).catch (function(err) {
expect(err.message).to.equal('Task (Work) is not associated to Worker!');
});
});
......@@ -567,7 +567,7 @@ describe(Support.getTestDialectTeaser('Model'), function() {
it('returns the associated task via worker.task', function() {
return this.Worker.find({
where: { name: 'worker' },
include: [{ daoFactory: this.Task, as: 'ToDo' }]
include: [{ model: this.Task, as: 'ToDo' }]
}).then(function(worker) {
expect(worker).to.exist;
expect(worker.ToDo).to.exist;
......@@ -648,7 +648,7 @@ describe(Support.getTestDialectTeaser('Model'), function() {
where: {
name: 'Boris'
},
include: [self.PhoneNumber, { daoFactory: self.Photo, as: 'Photos' }]
include: [self.PhoneNumber, { model: self.Photo, as: 'Photos' }]
}).then(function(fetchedContact) {
expect(fetchedContact).to.exist;
expect(fetchedContact.Photos.length).to.equal(1);
......@@ -720,7 +720,7 @@ describe(Support.getTestDialectTeaser('Model'), function() {
it('throws an error if alias is not associated', function() {
var self = this;
return self.Worker.find({ include: [{ daoFactory: self.Task, as: 'Work' }] }).catch (function(err) {
return self.Worker.find({ include: [{ model: self.Task, as: 'Work' }] }).catch (function(err) {
expect(err.message).to.equal('Task (Work) is not associated to Worker!');
});
});
......@@ -728,7 +728,7 @@ describe(Support.getTestDialectTeaser('Model'), function() {
it('returns the associated task via worker.task', function() {
return this.Worker.find({
where: { name: 'worker' },
include: [{ daoFactory: this.Task, as: 'ToDos' }]
include: [{ model: this.Task, as: 'ToDos' }]
}).then(function(worker) {
expect(worker).to.exist;
expect(worker.ToDos).to.exist;
......
......@@ -630,7 +630,7 @@ describe(Support.getTestDialectTeaser('Model'), function() {
it('throws an error if alias is not associated', function() {
var self = this;
return self.Worker.findAll({ include: [{ daoFactory: self.Task, as: 'Work' }] }).catch (function(err) {
return self.Worker.findAll({ include: [{ model: self.Task, as: 'Work' }] }).catch (function(err) {
expect(err.message).to.equal('Task (Work) is not associated to Worker!');
});
});
......@@ -638,7 +638,7 @@ describe(Support.getTestDialectTeaser('Model'), function() {
it('returns the associated task via worker.task', function() {
return this.Worker.findAll({
where: { name: 'worker' },
include: [{ daoFactory: this.Task, as: 'ToDo' }]
include: [{ model: this.Task, as: 'ToDo' }]
}).then(function(workers) {
expect(workers).to.exist;
expect(workers[0].ToDo).to.exist;
......@@ -722,7 +722,7 @@ describe(Support.getTestDialectTeaser('Model'), function() {
it('throws an error if alias is not associated', function() {
var self = this;
return self.Worker.findAll({ include: [{ daoFactory: self.Task, as: 'Work' }] }).catch (function(err) {
return self.Worker.findAll({ include: [{ model: self.Task, as: 'Work' }] }).catch (function(err) {
expect(err.message).to.equal('Task (Work) is not associated to Worker!');
});
});
......@@ -730,7 +730,7 @@ describe(Support.getTestDialectTeaser('Model'), function() {
it('returns the associated task via worker.task', function() {
return this.Worker.findAll({
where: { name: 'worker' },
include: [{ daoFactory: this.Task, as: 'ToDos' }]
include: [{ model: this.Task, as: 'ToDos' }]
}).then(function(workers) {
expect(workers).to.exist;
expect(workers[0].ToDos).to.exist;
......@@ -741,7 +741,7 @@ describe(Support.getTestDialectTeaser('Model'), function() {
it('returns the associated task via worker.task when daoFactory is aliased with model', function() {
return this.Worker.findAll({
where: { name: 'worker' },
include: [{ daoFactory: this.Task, as: 'ToDos' }]
include: [{ model: this.Task, as: 'ToDos' }]
}).then(function(workers) {
expect(workers[0].ToDos[0].title).to.equal('homework');
});
......
'use strict';
/* jshint -W030 */
/* jshint -W110 */
var chai = require('chai')
, Sequelize = require('../../../../index')
, expect = chai.expect
, Promise = Sequelize.Promise
, Support = require(__dirname + '/../../support');
describe(Support.getTestDialectTeaser('Model'), function() {
describe('scope', function () {
describe('associations', function () {
beforeEach(function () {
this.ScopeMe = this.sequelize.define('ScopeMe', {
username: Sequelize.STRING,
email: Sequelize.STRING,
access_level: Sequelize.INTEGER,
other_value: Sequelize.INTEGER,
parent_id: Sequelize.INTEGER
}, {
defaultScope: {
where: {
access_level: {
gte: 5
}
}
},
scopes: {
highValue: {
where: {
other_value: {
gte: 10
}
}
},
isTony: {
where: {
username: 'tony'
}
},
actualValue: function(value) {
return {
where: {
other_value: value
}
};
},
lowAccess: {
where: {
access_level: {
lte: 5
}
}
},
escape: {
where: {
username: "escape'd"
}
}
}
});
this.Company = this.sequelize.define('company', {
active: Sequelize.BOOLEAN
}, {
defaultScope: {
where: { active: true }
},
scopes: {
notActive: {
where: {
active: false
}
},
users: {
include: [
{ model: this.ScopeMe.unscoped(), as: 'users'}
]
},
reversed: {
order: [['id', 'DESC']]
}
}
});
this.Project = this.sequelize.define('Project');
this.Project.belongsToMany(this.Company, { through: 'CompanyProjects' });
this.Company.belongsToMany(this.Project, { through: 'CompanyProjects' });
this.ScopeMe.belongsTo(this.Company);
this.UserAssociation = this.Company.hasMany(this.ScopeMe, { as: 'users'});
return this.sequelize.sync({force: true}).bind(this).then(function() {
return Promise.all([
this.ScopeMe.create({username: 'dan', email: 'dan@sequelizejs.com', access_level: 5, other_value: 10, parent_id: 1}),
this.ScopeMe.create({username: 'tobi', email: 'tobi@fakeemail.com', access_level: 10, other_value: 11, parent_id: 2}),
this.ScopeMe.create({username: 'tony', email: 'tony@sequelizejs.com', access_level: 3, other_value: 7, parent_id: 1}),
this.ScopeMe.create({username: 'fred', email: 'fred@foobar.com', access_level: 3, other_value: 7, parent_id: 1}),
this.Company.create({active:true}),
this.Company.create({active:false}),
this.ScopeMe.create({username: 'bob', email: 'bob@foobar.com', access_level: 1, other_value: 9, parent_id: 5}),
]);
}).spread(function (u1, u2, u3, u4, c1, c2, u5) {
return Promise.all([
c1.setUsers([u1, u2, u3, u4]),
c2.setUsers([u5])
]);
});
});
describe('include', function () {
describe('target', function () {
it('should scope columns properly', function () {
// Will error with ambigous column if id is not scoped properly to `Company`.`id`
return expect(this.Company.findAll({
where: { id: 1},
include: [this.UserAssociation]
})).to.be.resolved;
});
it('should apply default scope when including an associations', function () {
return this.Company.findAll({
include: [this.UserAssociation]
}).get(0).then(function (company) {
expect(company.users).to.have.length(2);
});
});
it('should apply default scope when including a model', function () {
return this.Company.findAll({
include: [{ model: this.ScopeMe, as: 'users'}]
}).get(0).then(function (company) {
expect(company.users).to.have.length(2);
});
});
it('should be able to include a scoped model', function () {
return this.Company.findAll({
include: [{ model: this.ScopeMe.scope('isTony'), as: 'users'}]
}).get(0).then(function (company) {
expect(company.users).to.have.length(1);
expect(company.users[0].get('username')).to.equal('tony');
});
});
});
describe('source', function () {
it('should include model with scope', function () {
return this.Company.scope('users').findAll({ where: { id: 1 } }).get(0).then(function (company) {
expect(company.users).to.have.length(4);
});
});
it('should be able to override scoped include', function () {
return this.Company.scope('users').findAll({
include: [
{ association: this.UserAssociation, where: { username: 'dan'} }
]
}).get(0).then(function (company) {
expect(company.users).to.have.length(1);
expect(company.users[0].get('username')).to.equal('dan');
});
});
it('should be able to append to includes', function () {
// ? or should it
});
});
});
describe('get', function () {
beforeEach(function () {
return Promise.all([
this.Project.create(),
this.Company.unscoped().findAll()
]).spread(function (p, companies) {
return p.setCompanies(companies);
});
});
describe('it should be able to unscope', function () {
it('hasMany', function () {
return this.Company.find(1).then(function (company) {
return company.getUsers({ scope: false});
}).then(function (users) {
expect(users).to.have.length(4);
});
});
it('hasOne', function () {
});
it('belongsTo', function () {
return this.ScopeMe.unscoped().find({ where: { username: 'bob' }}).then(function (user) {
return user.getCompany({ scope: false });
}).then(function (company) {
expect(company).to.be.ok;
});
});
it('belongsToMany', function () {
return this.Project.findAll().get(0).then(function (p) {
return p.getCompanies({ scope: false});
}).then(function (companies) {
expect(companies).to.have.length(2);
});
});
});
describe('it should apply default scope', function () {
it('hasMany', function () {
return this.Company.find(1).then(function (company) {
return company.getUsers();
}).then(function (users) {
expect(users).to.have.length(2);
});
});
it('hasOne', function () {
});
it('hasMany double', function () {
// Do we need to - it's deprecated?
})
it('belongsTo', function () {
return this.ScopeMe.unscoped().find({ where: { username: 'bob' }}).then(function (user) {
return user.getCompany();
}).then(function (company) {
expect(company).not.to.be.ok;
});
});
it('belongsToMany', function () {
return this.Project.findAll().get(0).then(function (p) {
return p.getCompanies();
}).then(function (companies) {
expect(companies).to.have.length(1);
expect(companies[0].get('active')).to.be.ok;
});
});
});
describe('it should be able to apply another scope', function () {
it('hasMany', function () {
return this.Company.find(1).then(function (company) {
return company.getUsers({ scope: 'isTony'});
}).then(function (users) {
expect(users).to.have.length(1);
expect(users[0].get('username')).to.equal('tony');
});
});
it('hasOne', function () {
});
it('belongsTo', function () {
return this.ScopeMe.unscoped().find({ where: { username: 'bob' }}).then(function (user) {
return user.getCompany({ scope: 'notActive' });
}).then(function (company) {
expect(company).to.be.ok;
});
});
it('belongsToMany', function () {
return this.Project.findAll().get(0).then(function (p) {
return p.getCompanies({ scope: 'reversed' });
}).then(function (companies) {
expect(companies).to.have.length(2);
expect(companies[0].id).to.equal(2);
expect(companies[1].id).to.equal(1);
});
});
});
});
});
});
});
'use strict';
/* jshint -W030 */
/* jshint -W110 */
var chai = require('chai')
, Sequelize = require('../../../../index')
, expect = chai.expect
, Support = require(__dirname + '/../../support');
describe(Support.getTestDialectTeaser('Model'), function() {
describe('scope', function () {
describe('destroy', function () {
beforeEach(function () {
this.ScopeMe = this.sequelize.define('ScopeMe', {
username: Sequelize.STRING,
email: Sequelize.STRING,
access_level: Sequelize.INTEGER,
other_value: Sequelize.INTEGER
}, {
defaultScope: {
where: {
access_level: {
gte: 5
}
}
},
scopes: {
lowAccess: {
where: {
access_level: {
lte: 5
}
}
}
}
});
return this.sequelize.sync({force: true}).then(function() {
var records = [
{username: 'tony', email: 'tony@sequelizejs.com', access_level: 3, other_value: 7},
{username: 'tobi', email: 'tobi@fakeemail.com', access_level: 10, other_value: 11},
{username: 'dan', email: 'dan@sequelizejs.com', access_level: 5, other_value: 10},
{username: 'fred', email: 'fred@foobar.com', access_level: 3, other_value: 7}
];
return this.ScopeMe.bulkCreate(records);
}.bind(this));
});
it('should apply defaultScope', function () {
return this.ScopeMe.destroy({ where: {}}).bind(this).then(function () {
return this.ScopeMe.unscoped().findAll();
}).then(function (users) {
expect(users).to.have.length(2);
expect(users[0].get('username')).to.equal('tony');
expect(users[1].get('username')).to.equal('fred');
});
});
it('should be able to override default scope', function () {
return this.ScopeMe.destroy({ where: { access_level: { lt: 5 }}}).bind(this).then(function () {
return this.ScopeMe.unscoped().findAll();
}).then(function (users) {
expect(users).to.have.length(2);
expect(users[0].get('username')).to.equal('tobi');
expect(users[1].get('username')).to.equal('dan');
});
});
it('should be able to unscope destroy', function () {
return this.ScopeMe.unscoped().destroy({ where: {}}).bind(this).then(function() {
return expect(this.ScopeMe.unscoped().findAll()).to.eventually.have.length(0);
});
});
it('should be able to apply other scopes', function () {
return this.ScopeMe.scope('lowAccess').destroy({ where: {}}).bind(this).then(function () {
return this.ScopeMe.unscoped().findAll();
}).then(function (users) {
expect(users).to.have.length(1);
expect(users[0].get('username')).to.equal('tobi');
});
});
it('should be able to merge scopes with where', function () {
return this.ScopeMe.scope('lowAccess').destroy({ where: { username: 'dan'}}).bind(this).then(function () {
return this.ScopeMe.unscoped().findAll();
}).then(function (users) {
expect(users).to.have.length(3);
expect(users[0].get('username')).to.equal('tony');
expect(users[1].get('username')).to.equal('tobi');
expect(users[2].get('username')).to.equal('fred');
});
});
});
});
});
'use strict';
/* jshint -W030 */
/* jshint -W110 */
var chai = require('chai')
, _ = require('lodash')
, Sequelize = require('../../../../index')
, expect = chai.expect
, Support = require(__dirname + '/../../support');
describe(Support.getTestDialectTeaser('Model'), function() {
describe('scope', function () {
describe('update', function () {
beforeEach(function () {
this.ScopeMe = this.sequelize.define('ScopeMe', {
username: Sequelize.STRING,
email: Sequelize.STRING,
access_level: Sequelize.INTEGER,
other_value: Sequelize.INTEGER
}, {
defaultScope: {
where: {
access_level: {
gte: 5
}
}
},
scopes: {
lowAccess: {
where: {
access_level: {
lte: 5
}
}
}
}
});
return this.sequelize.sync({force: true}).then(function() {
var records = [
{username: 'tony', email: 'tony@sequelizejs.com', access_level: 3, other_value: 7},
{username: 'tobi', email: 'tobi@fakeemail.com', access_level: 10, other_value: 11},
{username: 'dan', email: 'dan@sequelizejs.com', access_level: 5, other_value: 10},
{username: 'fred', email: 'fred@foobar.com', access_level: 3, other_value: 7}
];
return this.ScopeMe.bulkCreate(records);
}.bind(this));
});
it('should apply defaultScope', function () {
return this.ScopeMe.update({ username: 'ruben' }, { where: {}}).bind(this).then(function () {
return this.ScopeMe.unscoped().findAll({ where: { username: 'ruben' }});
}).then(function (users) {
expect(users).to.have.length(2);
expect(users[0].get('email')).to.equal('tobi@fakeemail.com');
expect(users[1].get('email')).to.equal('dan@sequelizejs.com');
});
});
it('should be able to override default scope', function () {
return this.ScopeMe.update({ username: 'ruben' }, { where: { access_level: { lt: 5 }}}).bind(this).then(function () {
return this.ScopeMe.unscoped().findAll({ where: { username: 'ruben' }});
}).then(function (users) {
expect(users).to.have.length(2);
expect(users[0].get('email')).to.equal('tony@sequelizejs.com');
expect(users[1].get('email')).to.equal('fred@foobar.com');
});
});
it('should be able to unscope destroy', function () {
return this.ScopeMe.unscoped().update({ username: 'ruben' }, { where: {}}).bind(this).then(function() {
return this.ScopeMe.unscoped().findAll();
}).then(function (rubens) {
expect(_.every(rubens, function (r) {
return r.get('username') === 'ruben';
})).to.be.true;
});
});
it('should be able to apply other scopes', function () {
return this.ScopeMe.scope('lowAccess').update({ username: 'ruben' }, { where: {}}).bind(this).then(function () {
return this.ScopeMe.unscoped().findAll({ where: { username: { $ne: 'ruben' }}});
}).then(function (users) {
expect(users).to.have.length(1);
expect(users[0].get('email')).to.equal('tobi@fakeemail.com');
});
});
it('should be able to merge scopes with where', function () {
return this.ScopeMe.scope('lowAccess').update({ username: 'ruben' }, { where: { username: 'dan'}}).bind(this).then(function () {
return this.ScopeMe.unscoped().findAll({ where: { username: 'ruben' }});
}).then(function (users) {
expect(users).to.have.length(1);
expect(users[0].get('email')).to.equal('dan@sequelizejs.com');
});
});
});
});
});
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!