Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: fixes gets on many-to-many with non-primary target key #11587 #11778

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions lib/associations/belongs-to-many.js
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,7 @@ class BelongsToMany extends Association {
});
this.oneFromSource = new HasOne(this.source, this.through.model, {
foreignKey: this.foreignKey,
sourceKey: this.sourceKey,
as: this.through.model.name
});

Expand All @@ -366,6 +367,7 @@ class BelongsToMany extends Association {
});
this.oneFromTarget = new HasOne(this.target, this.through.model, {
foreignKey: this.otherKey,
sourceKey: this.targetKey,
as: this.through.model.name
});

Expand All @@ -376,6 +378,7 @@ class BelongsToMany extends Association {

this.paired.oneFromTarget = new HasOne(this.paired.target, this.paired.through.model, {
foreignKey: this.paired.otherKey,
sourceKey: this.paired.targetKey,
as: this.paired.through.model.name
});
}
Expand Down
79 changes: 79 additions & 0 deletions test/integration/associations/belongs-to-many.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -731,6 +731,85 @@ describe(Support.getTestDialectTeaser('BelongsToMany'), () => {
});
});

it('supports non primary key attributes for joins for getting associations (sourceKey/targetKey)', function() {
const User = this.sequelize.define('User', {
userId: {
type: DataTypes.UUID,
allowNull: false,
primaryKey: true,
defaultValue: DataTypes.UUIDV4
},
userSecondId: {
type: DataTypes.UUID,
allowNull: false,
defaultValue: DataTypes.UUIDV4,
field: 'user_second_id'
}
}, {
tableName: 'tbl_user',
indexes: [
{
unique: true,
fields: ['user_second_id']
}
]
});

const Group = this.sequelize.define('Group', {
groupId: {
type: DataTypes.UUID,
allowNull: false,
primaryKey: true,
defaultValue: DataTypes.UUIDV4
},
groupSecondId: {
type: DataTypes.UUID,
allowNull: false,
defaultValue: DataTypes.UUIDV4,
field: 'group_second_id'
}
}, {
tableName: 'tbl_group',
indexes: [
{
unique: true,
fields: ['group_second_id']
}
]
});

User.belongsToMany(Group, { through: 'usergroups', sourceKey: 'userSecondId', targetKey: 'groupSecondId' });
Group.belongsToMany(User, { through: 'usergroups', sourceKey: 'groupSecondId', targetKey: 'userSecondId' });

return this.sequelize.sync({ force: true }).then(() => {
return Promise.join(
User.create(),
User.create(),
Group.create(),
Group.create()
).then(([user1, user2, group1, group2]) => {
return Promise.join(user1.addGroup(group1), user2.addGroup(group2))
.then(() => {
return Promise.join(
user1.getGroups(),
user2.getGroups(),
group1.getUsers(),
group2.getUsers()
);
}).then(([groups1, groups2, users1, users2]) => {
expect(groups1.length).to.be.equal(1);
expect(groups1[0].id).to.be.equal(group1.id);
expect(groups2.length).to.be.equal(1);
expect(groups2[0].id).to.be.equal(group2.id);
expect(users1.length).to.be.equal(1);
expect(users1[0].id).to.be.equal(user1.id);
expect(users2.length).to.be.equal(1);
expect(users2[0].id).to.be.equal(user2.id);
});
});
});
});

it('supports non primary key attributes for joins (custom foreignKey)', function() {
const User = this.sequelize.define('User', {
id: {
Expand Down
41 changes: 40 additions & 1 deletion test/unit/associations/belongs-to-many.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ describe(Support.getTestDialectTeaser('belongsToMany'), () => {
expect(Object.keys(ProductTag.rawAttributes)).to.deep.equal(['id', 'priority', 'productId', 'tagId']);
});

it('should setup hasOne relations to source and target from join model with defined foreign/other keys', function() {
it('should setup hasMany relations to source and target from join model with defined foreign/other keys', function() {
const Product = this.sequelize.define('Product', {
title: DataTypes.STRING
}),
Expand Down Expand Up @@ -406,6 +406,45 @@ describe(Support.getTestDialectTeaser('belongsToMany'), () => {
expect(Object.keys(ProductTag.rawAttributes)).to.deep.equal(['id', 'priority', 'productId', 'tagId']);
});

it('should setup hasOne relations to source and target from join model with defined source keys', function() {
const Product = this.sequelize.define('Product', {
title: DataTypes.STRING,
productSecondaryId: DataTypes.STRING
}),
Tag = this.sequelize.define('Tag', {
name: DataTypes.STRING,
tagSecondaryId: DataTypes.STRING
}),
ProductTag = this.sequelize.define('ProductTag', {
id: {
primaryKey: true,
type: DataTypes.INTEGER,
autoIncrement: true
},
priority: DataTypes.INTEGER
}, {
timestamps: false
});

Product.Tags = Product.belongsToMany(Tag, { through: ProductTag, sourceKey: 'productSecondaryId' });
Tag.Products = Tag.belongsToMany(Product, { through: ProductTag, sourceKey: 'tagSecondaryId' });

expect(Product.Tags.oneFromSource).to.be.an.instanceOf(HasOne);
expect(Product.Tags.oneFromTarget).to.be.an.instanceOf(HasOne);

expect(Tag.Products.oneFromSource).to.be.an.instanceOf(HasOne);
expect(Tag.Products.oneFromTarget).to.be.an.instanceOf(HasOne);

expect(Tag.Products.oneFromSource.sourceKey).to.equal(Tag.Products.sourceKey);
expect(Tag.Products.oneFromTarget.sourceKey).to.equal(Tag.Products.targetKey);

expect(Product.Tags.oneFromSource.sourceKey).to.equal(Product.Tags.sourceKey);
expect(Product.Tags.oneFromTarget.sourceKey).to.equal(Product.Tags.targetKey);

expect(Object.keys(ProductTag.rawAttributes).length).to.equal(4);
expect(Object.keys(ProductTag.rawAttributes)).to.deep.equal(['id', 'priority', 'ProductProductSecondaryId', 'TagTagSecondaryId']);
});

it('should setup belongsTo relations to source and target from join model with only foreign keys defined', function() {
const Product = this.sequelize.define('Product', {
title: DataTypes.STRING
Expand Down