From d0f9b760f5a58228c66580ecf80611a21f08c5c9 Mon Sep 17 00:00:00 2001 From: sklyukin Date: Tue, 1 Sep 2015 01:24:47 +0500 Subject: [PATCH] primaryKey for hasMany and belongsTo relations --- lib/relation-definition.js | 20 +++---- test/relations.test.js | 113 ++++++++++++++++++++++++++++++++++--- 2 files changed, 115 insertions(+), 18 deletions(-) diff --git a/lib/relation-definition.js b/lib/relation-definition.js index 98f61b39..a94aa6bc 100644 --- a/lib/relation-definition.js +++ b/lib/relation-definition.js @@ -588,7 +588,7 @@ RelationDefinition.hasMany = function hasMany(modelFrom, modelTo, params) { var fk = params.foreignKey || i8n.camelize(thisClassName + '_id', true); var keyThrough = params.keyThrough || i8n.camelize(modelTo.modelName + '_id', true); - var idName = modelFrom.dataSource.idName(modelFrom.modelName) || 'id'; + var pkName = params.primaryKey || modelFrom.dataSource.idName(modelFrom.modelName) || 'id'; var discriminator, polymorphic; if (params.polymorphic) { @@ -610,7 +610,7 @@ RelationDefinition.hasMany = function hasMany(modelFrom, modelTo, params) { name: relationName, type: RelationTypes.hasMany, modelFrom: modelFrom, - keyFrom: idName, + keyFrom: pkName, keyTo: fk, modelTo: modelTo, multiple: true, @@ -629,7 +629,7 @@ RelationDefinition.hasMany = function hasMany(modelFrom, modelTo, params) { // obviously, modelTo should have attribute called `fk` // for polymorphic relations, it is assumed to share the same fk type for all // polymorphic models - modelTo.dataSource.defineForeignKey(modelTo.modelName, fk, modelFrom.modelName); + modelTo.dataSource.defineForeignKey(modelTo.modelName, fk, modelFrom.modelName, pkName); } var scopeMethods = { @@ -680,7 +680,7 @@ RelationDefinition.hasMany = function hasMany(modelFrom, modelTo, params) { defineScope(modelFrom.prototype, params.through || modelTo, relationName, function () { var filter = {}; filter.where = {}; - filter.where[fk] = this[idName]; + filter.where[fk] = this[pkName]; definition.applyScope(this, filter); @@ -1216,7 +1216,7 @@ RelationDefinition.belongsTo = function (modelFrom, modelTo, params) { modelTo = lookupModelTo(modelFrom, modelTo, params); } - var idName, relationName, fk; + var pkName, relationName, fk; if (params.polymorphic) { relationName = params.as || (typeof modelTo === 'string' ? modelTo : null); // initially @@ -1229,7 +1229,7 @@ RelationDefinition.belongsTo = function (modelFrom, modelTo, params) { modelTo = null; // will lookup dynamically - idName = params.idName || 'id'; + pkName = params.primaryKey || params.idName || 'id'; relationName = params.as || polymorphic.as; // finally fk = polymorphic.foreignKey; discriminator = polymorphic.discriminator; @@ -1237,16 +1237,16 @@ RelationDefinition.belongsTo = function (modelFrom, modelTo, params) { if (polymorphic.idType) { // explicit key type modelFrom.dataSource.defineProperty(modelFrom.modelName, fk, { type: polymorphic.idType, index: true }); } else { // try to use the same foreign key type as modelFrom - modelFrom.dataSource.defineForeignKey(modelFrom.modelName, fk, modelFrom.modelName); + modelFrom.dataSource.defineForeignKey(modelFrom.modelName, fk, modelFrom.modelName, pkName); } modelFrom.dataSource.defineProperty(modelFrom.modelName, discriminator, { type: 'string', index: true }); } else { - idName = modelTo.dataSource.idName(modelTo.modelName) || 'id'; + pkName = params.primaryKey || modelTo.dataSource.idName(modelTo.modelName) || 'id'; relationName = params.as || i8n.camelize(modelTo.modelName, true); fk = params.foreignKey || relationName + 'Id'; - modelFrom.dataSource.defineForeignKey(modelFrom.modelName, fk, modelTo.modelName); + modelFrom.dataSource.defineForeignKey(modelFrom.modelName, fk, modelTo.modelName, pkName); } var definition = modelFrom.relations[relationName] = new RelationDefinition({ @@ -1254,7 +1254,7 @@ RelationDefinition.belongsTo = function (modelFrom, modelTo, params) { type: RelationTypes.belongsTo, modelFrom: modelFrom, keyFrom: fk, - keyTo: idName, + keyTo: pkName, modelTo: modelTo, multiple: false, properties: params.properties, diff --git a/test/relations.test.js b/test/relations.test.js index f0b548e7..12fb0ec9 100644 --- a/test/relations.test.js +++ b/test/relations.test.js @@ -3113,7 +3113,7 @@ describe('relations', function () { Boss = db.define('Boss', {boardMembersNumber: Number, companyId: String}); }); - it('can be declared with non standard foreignKey', function () { + it('relation can be declared with primaryKey', function () { CompanyBoard.hasOne(Boss, { properties: {membersNumber: 'boardMembersNumber'}, primaryKey: 'companyId', @@ -3124,7 +3124,7 @@ describe('relations', function () { }); it('can be used to query data', function (done) { - db.automigrate(function () { + db.automigrate(['CompanyBoard', 'Boss'],function () { CompanyBoard.create({membersNumber: 7, companyId: 'Company1'}, function (e, companyBoard) { companyBoardId = companyBoard.id; should.not.exist(e); @@ -3145,22 +3145,22 @@ describe('relations', function () { }); }); - it('should destroy the related item on scope', function(done) { - CompanyBoard.findById(companyBoardId, function(e, companyBoard) { + it('should destroy the related item on scope', function (done) { + CompanyBoard.findById(companyBoardId, function (e, companyBoard) { should.not.exist(e); should.exist(companyBoard); - companyBoard.boss.destroy(function(err) { + companyBoard.boss.destroy(function (err) { should.not.exist(e); done(); }); }); }); - it('should get the related item on scope - verify', function(done) { - CompanyBoard.findById(companyBoardId, function(e, companyBoard) { + it('should get the related item on scope - verify', function (done) { + CompanyBoard.findById(companyBoardId, function (e, companyBoard) { should.not.exist(e); should.exist(companyBoard); - companyBoard.boss(function(err, act) { + companyBoard.boss(function (err, act) { should.not.exist(e); should.not.exist(act); done(); @@ -3169,6 +3169,103 @@ describe('relations', function () { }); }); + + describe('hasMany with primaryKey different from model PK', function () { + var Employee, Boss; + var COMPANY_ID = "Company1"; + + before(function () { + db = getSchema(); + Employee = db.define('Employee', {name: String, companyId: String}); + Boss = db.define('Boss', {address: String, companyId: String}); + }); + + it('relation can be declared with primaryKey', function () { + Boss.hasMany(Employee, { + primaryKey: 'companyId', + foreignKey: 'companyId' + }); + (new Boss()).employees.should.be.an.instanceOf(Function); + }); + + it('can be used to query employees for boss', function () { + return db.automigrate(['Employee', 'Boss']).then(function () { + return Boss.create({address: 'testAddress', companyId: COMPANY_ID}) + .then(function (boss) { + should.exist(boss); + should.exist(boss.employees); + return boss.employees.create([{name: 'a01'}, {name: 'a02'}]) + .then(function (employees) { + should.exists(employees); + return boss.employees(); + }).then(function (employees) { + var employee = employees[0]; + should.exist(employee); + employees.length.should.equal(2); + employee.should.be.an.instanceOf(Employee); + employee.companyId.should.be.equal(boss.companyId); + return employees; + }) + }); + }); + }); + + it('can be used to query employees for boss2', function () { + return db.automigrate(['Employee', 'Boss']).then(function () { + return Boss.create({address: 'testAddress', companyId: COMPANY_ID}) + .then(function (boss) { + return Employee.create({name: 'a01', companyId: COMPANY_ID}) + .then(function (employee) { + should.exist(employee); + return boss.employees.getAsync(); + }).then(function (employees) { + should.exists(employees); + employees.length.should.equal(1); + }) + }); + }); + }); + }); + + + describe('belongsTo with primaryKey different from model PK', function () { + var Employee, Boss; + var COMPANY_ID = "Company1"; + var bossId; + + before(function () { + db = getSchema(); + Employee = db.define('Employee', {name: String, companyId: String}); + Boss = db.define('Boss', {address: String, companyId: String}); + }); + + it('relation can be declared with primaryKey', function () { + Employee.belongsTo(Boss, { + primaryKey: 'companyId', + foreignKey: 'companyId' + }); + (new Employee()).boss.should.be.an.instanceOf(Function); + }); + + it('can be used to query data', function () { + return db.automigrate(['Employee', 'Boss']).then(function () { + return Boss.create({address: 'testAddress', companyId: COMPANY_ID}) + .then(function (boss) { + bossId = boss.id; + return Employee.create({name: 'a', companyId: COMPANY_ID}) + }) + .then(function (employee) { + should.exists(employee); + return employee.boss.getAsync(); + }) + .then(function (boss) { + should.exists(boss); + boss.id.should.equal(bossId); + }); + }); + }); + }); + describe('hasAndBelongsToMany', function () { var Article, TagName, ArticleTag; it('can be declared', function (done) {