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

Commit 09ac0969 by Mick Hansen

Merge pull request #1194 from mickhansen/has-many-through-self-association-fix

Add test and fix for hasMany selfAssociation with through option (Fixes #1184)
2 parents 9a219699 033e0939
...@@ -21,7 +21,7 @@ module.exports = (function() { ...@@ -21,7 +21,7 @@ module.exports = (function() {
, options = _options || {} , options = _options || {}
, queryOptions = {} , queryOptions = {}
, targetAssociation = self.association.targetAssociation , targetAssociation = self.association.targetAssociation
//fully qualify //fully qualify
var instancePrimaryKeys = Object.keys(self.instance.daoFactory.primaryKeys) var instancePrimaryKeys = Object.keys(self.instance.daoFactory.primaryKeys)
, instancePrimaryKey = instancePrimaryKeys.length > 0 ? instancePrimaryKeys[0] : 'id' , instancePrimaryKey = instancePrimaryKeys.length > 0 ? instancePrimaryKeys[0] : 'id'
......
...@@ -56,31 +56,38 @@ module.exports = (function() { ...@@ -56,31 +56,38 @@ module.exports = (function() {
} }
/* /*
* Find partner DAOFactory if present, to identify double linked association * If self association, this association is target association
*/ */
if (this.through) { if (this.isSelfAssociation) {
_.each(this.target.associations, function (association, accessor) { this.targetAssociation = this
if (self.source === association.target) { }
var paired = false
/*
// If through is default, we determine pairing by the accesor value (i.e. DAOFactory's using as won't pair, but regular ones will) * Else find partner DAOFactory if present, to identify double linked association
if (self.through === true && accessor === self.associationAccessor) { */
paired = true else if (this.through) {
} _.each(this.target.associations, function (association, accessor) {
// If through is not default, determine pairing by through value (model/string) if (self.source === association.target) {
if (self.through !== true && self.options.through === association.options.through) { var paired = false
paired = true
} // If through is default, we determine pairing by the accesor value (i.e. DAOFactory's using as won't pair, but regular ones will)
// If paired, set properties identifying both associations as double linked, and allow them to each eachtoerh if (self.through === true && accessor === self.associationAccessor) {
if (paired) { paired = true
self.doubleLinked = true
association.doubleLinked = true
self.targetAssociation = association
association.targetAssociation = self
}
} }
}) // If through is not default, determine pairing by through value (model/string)
if (self.through !== true && self.options.through === association.options.through) {
paired = true
}
// If paired, set properties identifying both associations as double linked, and allow them to each eachtoerh
if (paired) {
self.doubleLinked = true
association.doubleLinked = true
self.targetAssociation = association
association.targetAssociation = self
}
}
})
} }
/* /*
...@@ -90,13 +97,14 @@ module.exports = (function() { ...@@ -90,13 +97,14 @@ module.exports = (function() {
if (this.through === true) { if (this.through === true) {
this.through = this.combinedTableName this.through = this.combinedTableName
} }
if (typeof this.through === "string") { }
this.through = this.sequelize.define(this.through, {}, _.extend(this.options, {
tableName: this.through
}))
this.targetAssociation.through = this.through if (typeof this.through === "string") {
} this.through = this.sequelize.define(this.through, {}, _.extend(this.options, {
tableName: this.through
}))
if (this.targetAssociation) this.targetAssociation.through = this.through
} }
this.options.tableName = this.combinedName = (this.through === Object(this.through) ? this.through.tableName : this.through) this.options.tableName = this.combinedName = (this.through === Object(this.through) ? this.through.tableName : this.through)
...@@ -238,7 +246,7 @@ module.exports = (function() { ...@@ -238,7 +246,7 @@ module.exports = (function() {
return new Utils.CustomEventEmitter(function(emitter) { return new Utils.CustomEventEmitter(function(emitter) {
instance[self.accessors.get]() instance[self.accessors.get]()
.success(function(oldAssociatedObjects) { .success(function(oldAssociatedObjects) {
var Class = self.doubleLinked ? HasManyMultiLinked : HasManySingleLinked var Class = Object(self.through) === self.through ? HasManyMultiLinked : HasManySingleLinked
new Class(self, instance).injectSetter(emitter, oldAssociatedObjects, newAssociatedObjects, defaultAttributes) new Class(self, instance).injectSetter(emitter, oldAssociatedObjects, newAssociatedObjects, defaultAttributes)
}) })
.proxy(emitter, {events: ['error', 'sql']}) .proxy(emitter, {events: ['error', 'sql']})
...@@ -258,7 +266,7 @@ module.exports = (function() { ...@@ -258,7 +266,7 @@ module.exports = (function() {
.proxy(emitter, {events: ['error', 'sql']}) .proxy(emitter, {events: ['error', 'sql']})
.success(function(currentAssociatedObjects) { .success(function(currentAssociatedObjects) {
if (currentAssociatedObjects.length === 0 || Object(self.through) === self.through) { if (currentAssociatedObjects.length === 0 || Object(self.through) === self.through) {
var Class = self.doubleLinked ? HasManyMultiLinked : HasManySingleLinked var Class = Object(self.through) === self.through ? HasManyMultiLinked : HasManySingleLinked
new Class(self, instance).injectAdder(emitter, newAssociatedObject, additionalAttributes, !!currentAssociatedObjects.length) new Class(self, instance).injectAdder(emitter, newAssociatedObject, additionalAttributes, !!currentAssociatedObjects.length)
} else { } else {
emitter.emit('success', newAssociatedObject); emitter.emit('success', newAssociatedObject);
......
...@@ -399,7 +399,7 @@ module.exports = (function() { ...@@ -399,7 +399,7 @@ module.exports = (function() {
optAttributes = optAttributes.concat(attributes) optAttributes = optAttributes.concat(attributes)
if (include.association.doubleLinked) { if (Object(include.association.through) === include.association.through) {
var primaryKeysSource = Object.keys(include.association.source.primaryKeys) var primaryKeysSource = Object.keys(include.association.source.primaryKeys)
, tableSource = parentTable , tableSource = parentTable
, identSource = include.association.identifier , identSource = include.association.identifier
......
...@@ -429,6 +429,19 @@ describe(Support.getTestDialectTeaser("HasMany"), function() { ...@@ -429,6 +429,19 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
}) })
}) })
}) // end optimization using bulk create, destroy and update }) // end optimization using bulk create, destroy and update
describe('selfAssociations', function () {
it('should work', function (done) {
var Group = this.sequelize.define('Group', {})
Group.hasMany(Group, { through: 'groups_outsourcing_companies', as: 'OutsourcingCompanies'});
this.sequelize.sync().done(function (err) {
expect(err).not.to.be.ok
done()
})
})
})
}) })
describe('(N:M)', function() { describe('(N:M)', function() {
......
...@@ -478,6 +478,40 @@ describe(Support.getTestDialectTeaser("Include"), function () { ...@@ -478,6 +478,40 @@ describe(Support.getTestDialectTeaser("Include"), function () {
}) })
}) })
}) })
it('should support self associated hasMany (with through) include', function (done) {
var Group = this.sequelize.define('Group', {
name: DataTypes.STRING
})
Group.hasMany(Group, { through: 'groups_outsourcing_companies', as: 'OutsourcingCompanies'});
this.sequelize.sync().done(function (err) {
Group.bulkCreate([
{name: 'SoccerMoms'},
{name: 'Coca Cola'},
{name: 'Dell'},
{name: 'Pepsi'}
]).done(function () {
Group.findAll().done(function (err, groups) {
groups[0].setOutsourcingCompanies(groups.slice(1)).done(function (err) {
expect(err).not.to.be.ok
Group.find({
where: {
id: groups[0].id,
},
include: [{model: Group, as: 'OutsourcingCompanies'}]
}).done(function (err, group) {
expect(err).not.to.be.ok
expect(group.outsourcingCompanies.length).to.equal(3)
done()
})
})
})
})
})
})
}) })
describe('findAll', function () { describe('findAll', function () {
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!