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

Commit 222fb13c by Mick Hansen

[feat] instance.update() will now also update values provided in beforeUpdate ho…

…oks if fields was not set by user
1 parent 23a1b086
......@@ -161,6 +161,18 @@ Hooks.addHook = function(hookType, name, fn) {
return this;
};
/*
* Check whether the mode has any hooks of this type
*
* @param {String} hookType
*
* @alias hasHooks
*/
Hooks.hasHook = function(hookType) {
return !!this.options.hooks[hookType].length;
};
Hooks.hasHooks = Hooks.hasHook;
/**
* A hook that is run before validation
* @param {String} name
......
......@@ -470,6 +470,8 @@ module.exports = (function() {
} else {
options.fields = _.intersection(this.changed(), Object.keys(this.Model.attributes));
}
options.defaultFields = options.fields;
}
if (options.returning === undefined) {
......@@ -501,11 +503,11 @@ module.exports = (function() {
options.fields.push(createdAtAttr);
}
return Promise.try(function() {
return Promise.bind(this).then(function() {
// Validate
if (options.validate) {
options.skip = _.difference(Object.keys(self.rawAttributes), options.fields);
return (options.hooks ? self.hookValidate(options) : self.validate(options)).then(function() {
options.skip = _.difference(Object.keys(this.Model.rawAttributes), options.fields);
return (options.hooks ? this.hookValidate(options) : this.validate(options)).then(function() {
delete options.skip;
});
}
......@@ -516,12 +518,12 @@ module.exports = (function() {
}
});
for (var attrName in self.Model.rawAttributes) {
for (var attrName in this.Model.rawAttributes) {
if (self.Model.rawAttributes.hasOwnProperty(attrName)) {
var definition = self.Model.rawAttributes[attrName]
var definition = this.Model.rawAttributes[attrName]
, isEnum = definition.type.toString() === DataTypes.ENUM.toString()
, isMySQL = ['mysql', 'mariadb'].indexOf(self.Model.modelManager.sequelize.options.dialect) !== -1
, ciCollation = !!self.Model.options.collate && self.Model.options.collate.match(/_ci$/i)
, isMySQL = ['mysql', 'mariadb'].indexOf(this.Model.modelManager.sequelize.options.dialect) !== -1
, ciCollation = !!this.Model.options.collate && this.Model.options.collate.match(/_ci$/i)
, valueOutOfScope;
// Unfortunately for MySQL CI collation we need to map/lowercase values again
......@@ -579,31 +581,50 @@ module.exports = (function() {
// Add the values to the Instance
self.dataValues = _.extend(self.dataValues, values);
return Promise.try(function() {
return Promise.bind(this).then(function() {
// Run before hook
if (options.hooks) {
return self.Model.runHooks('before' + hook, self, options).then(function() {
var hookFields
, ignoreChanged = _.difference(this.changed(), options.fields) // In case of update where it's only supposed to update the passed values and the hook values
, fieldsHadUpdatedAtAttr = updatedAtAttr && options.fields.indexOf(updatedAtAttr) !== -1;
if (updatedAtAttr && options.fields.indexOf(updatedAtAttr) !== -1) {
ignoreChanged = _.without(ignoreChanged, updatedAtAttr);
}
return this.Model.runHooks('before' + hook, this, options).bind(this).then(function() {
if (options.defaultFields && !this.isNewRecord) {
//options.fields = _.without.apply(_, [_.intersection(this.changed(), Object.keys(this.Model.attributes))].concat(ignoreChanged));
options.fields = _.difference(_.intersection(this.changed(), Object.keys(this.Model.attributes)), ignoreChanged);
if (fieldsHadUpdatedAtAttr && options.fields.indexOf(updatedAtAttr) === -1) {
options.fields.push(updatedAtAttr);
}
hookFields = _.difference(options.fields, options.defaultFields);
}
// dataValues might have changed inside the hook, rebuild the values hash
values = {};
options.fields.forEach(function(attr) {
if (self.dataValues[attr] !== undefined && !self.Model._isVirtualAttribute(attr)) {
values[attr] = self.dataValues[attr];
if (this.dataValues[attr] !== undefined && !this.Model._isVirtualAttribute(attr)) {
values[attr] = this.dataValues[attr];
}
// Field name mapping
if (self.Model.rawAttributes[attr].field && self.Model.rawAttributes[attr].field !== attr) {
values[self.Model.rawAttributes[attr].field] = values[attr];
if (this.Model.rawAttributes[attr].field && this.Model.rawAttributes[attr].field !== attr) {
values[this.Model.rawAttributes[attr].field] = values[attr];
delete values[attr];
}
});
}, this);
args[2] = values;
});
}
}).then(function() {
return self.QueryInterface[query].apply(self.QueryInterface, args)
.tap(function(result) {
.then(function(result) {
// Transfer database generated values (defaults, autoincrement, etc)
Object.keys(self.Model.rawAttributes).forEach(function (attr) {
if (self.Model.rawAttributes[attr].field &&
......@@ -616,6 +637,7 @@ module.exports = (function() {
values = _.extend(values, result.dataValues);
result.dataValues = _.extend(result.dataValues, values);
return result;
})
.tap(function(result) {
// Run after hook
......@@ -627,6 +649,7 @@ module.exports = (function() {
options.fields.forEach(function (field) {
result._previousDataValues[field] = result.dataValues[field];
});
self.isNewRecord = false;
return result;
});
});
......@@ -694,7 +717,10 @@ module.exports = (function() {
this.set(values, {attributes: options.fields});
if (!options.fields) options.fields = _.intersection(Object.keys(values), this.changed());
if (!options.fields) {
options.fields = _.intersection(Object.keys(values), this.changed());
options.defaultFields = options.fields;
}
return this.save(options);
};
......
......@@ -98,7 +98,7 @@ describe(Support.getTestDialectTeaser('Instance'), function() {
return user.save().then(function () {
user.set('validateTest', 5);
expect(user.changed('validateTest')).to.be.ok;
return user.updateAttributes({
return user.update({
validateCustom: '1'
});
}).then(function () {
......@@ -111,6 +111,37 @@ describe(Support.getTestDialectTeaser('Instance'), function() {
});
});
it('should update attributes added in hooks when default fields are used', function () {
var User = this.sequelize.define('User' + config.rand(), {
name: DataTypes.STRING,
bio: DataTypes.TEXT,
email: DataTypes.STRING
});
User.beforeUpdate(function(instance, options) {
instance.set('email', 'B');
});
return User.sync({force: true}).then(function() {
return User.create({
name: 'A',
bio: 'A',
email: 'A'
}).then(function (user) {
return user.update({
name: 'B',
bio: 'B'
});
}).then(function () {
return User.findOne({});
}).then(function (user) {
expect(user.get('name')).to.equal('B');
expect(user.get('bio')).to.equal('B');
expect(user.get('email')).to.equal('B');
});
});
});
it('should not set attributes that are not specified by fields', function () {
var User = this.sequelize.define('User' + config.rand(), {
name: DataTypes.STRING,
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!