Skip to content

Commit

Permalink
Merge pull request #2560 from simong/remove-multiple
Browse files Browse the repository at this point in the history
#2338 - Associations remove[AS][plural]
  • Loading branch information
mickhansen committed Nov 10, 2014
2 parents ffc50e1 + c967535 commit 1ce1127
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 3 deletions.
1 change: 1 addition & 0 deletions changelog.md
@@ -1,4 +1,5 @@
# Next
- [FEATURE] Added the possibility of removing multiple associations in 1 call [#2338](https://github.com/sequelize/sequelize/issues/2338)
- [BUG] Add support for `field` named the same as the attribute in `reload`, `bulkCreate` and `save` [#2348](https://github.com/sequelize/sequelize/issues/2348)
- [BUG] Copy the options object in association getters. [#2311](https://github.com/sequelize/sequelize/issues/2311)
- [BUG] `Model#destroy()` now supports `field`, this also fixes an issue with `N:M#removeAssociation` and `field`
Expand Down
24 changes: 24 additions & 0 deletions lib/associations/has-many.js
Expand Up @@ -176,6 +176,7 @@ module.exports = (function() {
add: 'add' + singular,
create: 'create' + singular,
remove: 'remove' + singular,
removeMultiple: 'remove' + plural,
hasSingle: 'has' + singular,
hasAll: 'has' + plural
};
Expand Down Expand Up @@ -483,6 +484,29 @@ module.exports = (function() {
});
};

obj[this.accessors.removeMultiple] = function(oldAssociatedObjects, options) {
var instance = this;
return instance[association.accessors.get]({}, options).then(function(currentAssociatedObjects) {
var newAssociations = [];

currentAssociatedObjects.forEach(function(association) {

// Determine is this is an association we want to remove
var obj = Utils._.find(oldAssociatedObjects, function(oldAssociatedObject) {
return Utils._.isEqual(oldAssociatedObject.identifiers, association.identifiers);
});

// This is not an association we want to remove. Add it back
// to the set of associations we will associate our instance with
if (!obj) {
newAssociations.push(association);
}
});

return instance[association.accessors.set](newAssociations, options);
});
};

return this;
};

Expand Down
3 changes: 2 additions & 1 deletion lib/associations/mixin.js
Expand Up @@ -187,7 +187,8 @@ Mixin.belongsTo = singleLinked(BelongsTo);
* * add[AS] - for example addPicture(instance, defaultAttributes|options). Add another associated object.
* * add[AS] [plural] - for example addPictures([instance1, instance2], defaultAttributes|options). Add some more associated objects.
* * create[AS] - for example createPicture(values, options). Build and save a new association.
* * remove[AS] - for example removePicture(instance). Remove a single association
* * remove[AS] - for example removePicture(instance). Remove a single association.
* * remove[AS] [plural] - for example removePictures(instance). Remove multiple association.
* * has[AS] - for example hasPicture(instance). Is source associated to this target?
* * has[AS] [plural] - for example hasPictures(instances). Is source associated to all these targets?
*
Expand Down
28 changes: 28 additions & 0 deletions test/associations/has-many.test.js
Expand Up @@ -1986,6 +1986,34 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
expect(tasks.length).to.equal(1);
});
});

it('should remove multiple entries without any attributes (and timestamps off) on the through model', function () {
var Worker = this.sequelize.define('Worker', {}, {timestamps: false})
, Task = this.sequelize.define('Task', {}, {timestamps: false})
, WorkerTasks = this.sequelize.define('WorkerTasks', {}, {timestamps: false});

Worker.hasMany(Task, { through: WorkerTasks });
Task.hasMany(Worker, { through: WorkerTasks });

// Test setup
return this.sequelize.sync().then(function() {
return Sequelize.Promise.all([
Worker.create({}),
Task.bulkCreate([{}, {}, {}]).then(function () {
return Task.findAll();
})
]);
}).spread(function (worker, tasks) {
// Set all tasks, then remove two tasks, then return all tasks
return worker.setTasks(tasks).then(function () {
return worker.removeTasks([tasks[0], tasks[1]]);
}).then(function () {
return worker.getTasks();
});
}).then(function (tasks) {
expect(tasks.length).to.equal(1);
});
});
});
});

Expand Down
7 changes: 6 additions & 1 deletion test/mysql/associations.test.js
Expand Up @@ -137,7 +137,12 @@ if (Support.dialectIsMySQL()) {
self.user.removeTask(self.tasks[0]).on('success', function() {
self.user.getTasks().on('success', function(_tasks) {
expect(_tasks.length).to.equal(self.tasks.length - 1)
done()
self.user.removeTasks([self.tasks[1], self.tasks[2]]).on('success', function() {
self.user.getTasks().on('success', function(_tasks) {
expect(_tasks).to.have.length(self.tasks.length - 3)
done()
})
})
})
})
})
Expand Down
7 changes: 6 additions & 1 deletion test/postgres/associations.test.js
Expand Up @@ -151,7 +151,12 @@ if (dialect.match(/^postgres/)) {
self.user.removeTask(self.tasks[0]).on('success', function() {
self.user.getTasks().on('success', function(_tasks) {
expect(_tasks).to.have.length(self.tasks.length - 1)
done()
self.user.removeTasks([self.tasks[1], self.tasks[2]]).on('success', function() {
self.user.getTasks().on('success', function(_tasks) {
expect(_tasks).to.have.length(self.tasks.length - 3)
done()
})
})
})
})
})
Expand Down

0 comments on commit 1ce1127

Please sign in to comment.