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

Commit a15d4233 by Nuno Sousa Committed by Jan Aagaard Meier

Fix `destroy()` affecting previous instances

1 parent 2e4b9b26
......@@ -133,7 +133,7 @@ Instance.prototype.where = function() {
if (_.size(where) === 0) {
return this.$modelOptions.whereCollection;
}
return where;
return Utils.mapWhereFieldNames(where, this.$Model);
};
Instance.prototype.toString = function () {
......@@ -821,11 +821,18 @@ Instance.prototype.destroy = function(options) {
return this.Model.runHooks('beforeDestroy', this, options);
}
}).then(function() {
var where = Utils.mapOptionFieldNames({ where: this.where() }, this.Model).where;
var where = this.where();
if (this.Model._timestampAttributes.deletedAt && options.force === false) {
this.setDataValue(this.Model._timestampAttributes.deletedAt, new Date());
return this.save(_.extend({}, options, {hooks : false}));
var field = this.Model.rawAttributes[this.Model._timestampAttributes.deletedAt].field || this.Model._timestampAttributes.deletedAt;
var values = {};
values[field] = new Date();
where[field] = null;
this.setDataValue(field, values[field]);
return this.sequelize.getQueryInterface().update(this, this.Model.getTableName(options), values, where, _.defaults({ hooks: false }, options));
} else {
return this.sequelize.getQueryInterface().delete(this, this.Model.getTableName(options), where, _.assign({ type: QueryTypes.DELETE, limit: null }, options));
}
......@@ -901,7 +908,7 @@ Instance.prototype.restore = function(options) {
* @return {Promise<this>}
*/
Instance.prototype.increment = function(fields, options) {
var identifier = Utils.mapOptionFieldNames({ where: this.where() }, this.Model).where
var identifier = this.where()
, updatedAtAttr = this.Model._timestampAttributes.updatedAt
, values = {}
, where;
......
......@@ -2249,10 +2249,14 @@ Model.prototype.destroy = function(options) {
// Run delete query (or update if paranoid)
if (self._timestampAttributes.deletedAt && !options.force) {
var attrValueHash = {}
, field = self.rawAttributes[self._timestampAttributes.deletedAt].field || self._timestampAttributes.deletedAt;
, deletedAtAttribute = self.rawAttributes[self._timestampAttributes.deletedAt]
, field = self.rawAttributes[self._timestampAttributes.deletedAt].field
, where = {};
where[field] = deletedAtAttribute.hasOwnProperty('defaultValue') ? deletedAtAttribute.defaultValue : null;
attrValueHash[field] = Utils.now(self.modelManager.sequelize.options.dialect);
return self.QueryInterface.bulkUpdate(self.getTableName(options), attrValueHash, options.where, options, self.rawAttributes);
return self.QueryInterface.bulkUpdate(self.getTableName(options), attrValueHash, _.defaults(where, options.where), options, self.rawAttributes);
} else {
return self.QueryInterface.bulkDelete(self.getTableName(options), options.where, options, self);
}
......
......@@ -112,40 +112,43 @@ var Utils = module.exports = {
}
if (options.where && _.isPlainObject(options.where)) {
var attributes = options.where
, attribute
, rawAttribute;
if (attributes) {
for (attribute in attributes) {
rawAttribute = Model.rawAttributes[attribute];
if (rawAttribute && rawAttribute.field !== rawAttribute.fieldName) {
attributes[rawAttribute.field] = attributes[attribute];
delete attributes[attribute];
}
options.where = Utils.mapWhereFieldNames(options.where, Model);
}
if (_.isPlainObject(attributes[attribute])) {
attributes[attribute] = Utils.mapOptionFieldNames({
where: attributes[attribute]
}, Model).where;
}
return options;
},
if (Array.isArray(attributes[attribute])) {
attributes[attribute] = attributes[attribute].map(function (where) {
if (_.isPlainObject(where)) {
return Utils.mapOptionFieldNames({
where: where
}, Model).where;
}
mapWhereFieldNames: function (attributes, Model) {
var attribute
, rawAttribute;
return where;
});
}
if (attributes) {
for (attribute in attributes) {
rawAttribute = Model.rawAttributes[attribute];
if (rawAttribute && rawAttribute.field !== rawAttribute.fieldName) {
attributes[rawAttribute.field] = attributes[attribute];
delete attributes[attribute];
}
if (_.isPlainObject(attributes[attribute])) {
attributes[attribute] = Utils.mapOptionFieldNames({
where: attributes[attribute]
}, Model).where;
}
if (Array.isArray(attributes[attribute])) {
attributes[attribute] = attributes[attribute].map(function (where) {
if (_.isPlainObject(where)) {
return Utils.mapWhereFieldNames(where, Model);
}
return where;
});
}
}
}
return options;
return attributes;
},
/* Used to map field names in values */
......
......@@ -1662,8 +1662,12 @@ describe(Support.getTestDialectTeaser('Instance'), function() {
var self = this;
return this.ParanoidUser.create({ username: 'fnord' }).then(function() {
return self.ParanoidUser.findAll().then(function(users) {
return users[0].destroy().then(function(user) {
expect(user.deletedAt.getMonth).to.exist;
return users[0].destroy().then(function() {
expect(users[0].deletedAt.getMonth).to.exist;
return users[0].reload({ paranoid: false }).then(function(user) {
expect(user.deletedAt.getMonth).to.exist;
});
});
});
});
......@@ -1789,6 +1793,29 @@ describe(Support.getTestDialectTeaser('Instance'), function() {
});
}
it('does not set the deletedAt date in subsequent destroys if dao is paranoid', function() {
var UserDestroy = this.sequelize.define('UserDestroy', {
name: Support.Sequelize.STRING,
bio: Support.Sequelize.TEXT
}, { paranoid: true });
return UserDestroy.sync({ force: true }).then(function() {
return UserDestroy.create({name: 'hallo', bio: 'welt'}).then(function(user) {
return user.destroy().then(function() {
return user.reload({ paranoid: false }).then(function() {
var deletedAt = user.deletedAt;
return user.destroy().then(function() {
return user.reload({ paranoid: false }).then(function() {
expect(user.deletedAt).to.eql(deletedAt);
});
});
});
});
});
});
});
it('deletes a record from the database if dao is not paranoid', function() {
var UserDestroy = this.sequelize.define('UserDestroy', {
name: Support.Sequelize.STRING,
......
......@@ -180,8 +180,10 @@ describe(Support.getTestDialectTeaser('Model'), function() {
return UserTable.create({aNumber: 4}).then(function(user) {
expect(user.updatedOn).to.exist;
expect(user.dateCreated).to.exist;
return user.destroy().then(function(user) {
expect(user.deletedAtThisTime).to.exist;
return user.destroy().then(function() {
return user.reload({ paranoid: false }).then(function() {
expect(user.deletedAtThisTime).to.exist;
});
});
});
});
......@@ -208,8 +210,10 @@ describe(Support.getTestDialectTeaser('Model'), function() {
user.name = 'heho';
return user.save().then(function(user) {
expect(user.updatedAt).not.to.exist;
return user.destroy().then(function(user) {
expect(user.deletedAtThisTime).to.exist;
return user.destroy().then(function() {
return user.reload({ paranoid: false }).then(function() {
expect(user.deletedAtThisTime).to.exist;
});
});
});
});
......@@ -1327,6 +1331,35 @@ describe(Support.getTestDialectTeaser('Model'), function() {
});
});
it('does not set deletedAt for previously destroyed instances if paranoid is true', function() {
var User = this.sequelize.define('UserCol', {
secretValue: Sequelize.STRING,
username: Sequelize.STRING
}, { paranoid: true });
return User.sync({ force: true }).then(function() {
return User.bulkCreate([
{ username: 'Toni', secretValue: '42' },
{ username: 'Tobi', secretValue: '42' },
{ username: 'Max', secretValue: '42' }
]).then(function() {
return User.findById(1).then(function(user) {
return user.destroy().then(function() {
return user.reload({ paranoid: false }).then(function() {
var deletedAt = user.deletedAt;
return User.destroy({ where: { secretValue: '42' } }).then(function() {
return user.reload({ paranoid: false }).then(function() {
expect(user.deletedAt).to.eql(deletedAt);
});
});
});
});
});
});
});
});
describe("can't find records marked as deleted with paranoid being true", function() {
it('with the DAOFactory', function() {
var User = this.sequelize.define('UserCol', {
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!