diff --git a/lib/relation-definition.js b/lib/relation-definition.js index eb726ebd..b224faad 100644 --- a/lib/relation-definition.js +++ b/lib/relation-definition.js @@ -1105,9 +1105,11 @@ RelationDefinition.belongsTo = function (modelFrom, modelTo, params) { get: function() { var relation = new BelongsTo(definition, this); var relationMethod = relation.related.bind(relation); - relationMethod.create = relation.create.bind(relation); - relationMethod.build = relation.build.bind(relation); - if (definition.modelTo) { + relationMethod.update = relation.update.bind(relation); + relationMethod.destroy = relation.destroy.bind(relation); + if (!polymorphic) { + relationMethod.create = relation.create.bind(relation); + relationMethod.build = relation.build.bind(relation); relationMethod._targetClass = definition.modelTo.modelName; } return relationMethod; @@ -1160,6 +1162,36 @@ BelongsTo.prototype.build = function(targetModelData) { return new modelTo(targetModelData); }; +BelongsTo.prototype.update = function (targetModelData, cb) { + var definition = this.definition; + this.fetch(function(err, inst) { + if (inst instanceof ModelBaseClass) { + inst.updateAttributes(targetModelData, cb); + } else { + cb(new Error('BelongsTo relation ' + definition.name + + ' is empty')); + } + }); +}; + +BelongsTo.prototype.destroy = function (cb) { + var modelTo = this.definition.modelTo; + var modelInstance = this.modelInstance; + var fk = this.definition.keyFrom; + this.fetch(function(err, targetModel) { + if (targetModel instanceof ModelBaseClass) { + modelInstance[fk] = null; + modelInstance.save(function(err, targetModel) { + if (cb && err) return cb && cb(err); + cb && cb(err, targetModel); + }); + } else { + cb(new Error('BelongsTo relation ' + definition.name + + ' is empty')); + } + }); +}; + /** * Define the method for the belongsTo relation itself * It will support one of the following styles: @@ -1224,7 +1256,8 @@ BelongsTo.prototype.related = function (refresh, params) { var query = {where: {}}; query.where[pk] = modelInstance[fk]; - if (query.where[pk] === undefined) { + if (query.where[pk] === undefined + || query.where[pk] === null) { // Foreign key is undefined return process.nextTick(cb); } @@ -1373,6 +1406,8 @@ RelationDefinition.hasOne = function (modelFrom, modelTo, params) { var relationMethod = relation.related.bind(relation) relationMethod.create = relation.create.bind(relation); relationMethod.build = relation.build.bind(relation); + relationMethod.update = relation.update.bind(relation); + relationMethod.destroy = relation.destroy.bind(relation); relationMethod._targetClass = definition.modelTo.modelName; return relationMethod; } @@ -1436,6 +1471,29 @@ HasOne.prototype.create = function (targetModelData, cb) { }); }; +HasOne.prototype.update = function (targetModelData, cb) { + var definition = this.definition; + this.fetch(function(err, targetModel) { + if (targetModel instanceof ModelBaseClass) { + targetModel.updateAttributes(targetModelData, cb); + } else { + cb(new Error('HasOne relation ' + definition.name + + ' is empty')); + } + }); +}; + +HasOne.prototype.destroy = function (cb) { + this.fetch(function(err, targetModel) { + if (targetModel instanceof ModelBaseClass) { + targetModel.destroy(cb); + } else { + cb(new Error('HasOne relation ' + definition.name + + ' is empty')); + } + }); +}; + /** * Create a target model instance * @param {Object} targetModelData The target model data diff --git a/test/relations.test.js b/test/relations.test.js index 5ba2b01f..7751ee23 100644 --- a/test/relations.test.js +++ b/test/relations.test.js @@ -1112,6 +1112,8 @@ describe('relations', function () { describe('belongsTo', function () { var List, Item, Fear, Mind; + + var listId, itemId; it('can be declared in different ways', function () { List = db.define('List', {name: String}); @@ -1134,15 +1136,18 @@ describe('relations', function () { it('can be used to query data', function (done) { List.hasMany('todos', {model: Item}); db.automigrate(function () { - List.create(function (e, list) { + List.create({name: 'List 1'}, function (e, list) { + listId = list.id; should.not.exist(e); should.exist(list); - list.todos.create(function (err, todo) { + list.todos.create({name: 'Item 1'},function (err, todo) { + itemId = todo.id; todo.list(function (e, l) { should.not.exist(e); should.exist(l); l.should.be.an.instanceOf(List); todo.list().id.should.equal(l.id); + todo.list().name.should.equal('List 1'); done(); }); }); @@ -1164,6 +1169,55 @@ describe('relations', function () { }); }); }); + + it('should update related item on scope', function(done) { + Item.findById(itemId, function (e, todo) { + todo.list.update({name: 'List A'}, function(err, list) { + should.not.exist(err); + should.exist(list); + list.name.should.equal('List A'); + done(); + }); + }); + }); + + it('should get related item on scope', function(done) { + Item.findById(itemId, function (e, todo) { + todo.list(function(err, list) { + should.not.exist(err); + should.exist(list); + list.name.should.equal('List A'); + done(); + }); + }); + }); + + it('should destroy related item on scope', function(done) { + Item.findById(itemId, function (e, todo) { + todo.list.destroy(function(err) { + should.not.exist(err); + done(); + }); + }); + }); + + it('should get related item on scope - verify', function(done) { + Item.findById(itemId, function (e, todo) { + todo.list(function(err, list) { + should.not.exist(err); + should.not.exist(list); + done(); + }); + }); + }); + + it('should not have deleted related item', function(done) { + List.findById(listId, function (e, list) { + should.not.exist(e); + should.exist(list); + done(); + }); + }); }); @@ -1208,6 +1262,7 @@ describe('relations', function () { describe('hasOne', function () { var Supplier, Account; + var supplierId, accountId; before(function () { db = getSchema(); @@ -1222,13 +1277,14 @@ describe('relations', function () { }); it('can be used to query data', function (done) { - // Supplier.hasOne(Account); db.automigrate(function () { Supplier.create({name: 'Supplier 1'}, function (e, supplier) { + supplierId = supplier.id; should.not.exist(e); should.exist(supplier); supplier.account.create({accountNo: 'a01'}, function (err, account) { supplier.account(function (e, act) { + accountId = act.id; should.not.exist(e); should.exist(act); act.should.be.an.instanceOf(Account); @@ -1244,6 +1300,63 @@ describe('relations', function () { it('should set targetClass on scope property', function() { should.equal(Supplier.prototype.account._targetClass, 'Account'); }); + + it('should update the related item on scope', function(done) { + Supplier.findById(supplierId, function(e, supplier) { + should.not.exist(e); + should.exist(supplier); + supplier.account.update({supplierName: 'Supplier A'}, function(err, act) { + should.not.exist(e); + act.supplierName.should.equal('Supplier A'); + done(); + }); + }); + }); + + it('should get the related item on scope', function(done) { + Supplier.findById(supplierId, function(e, supplier) { + should.not.exist(e); + should.exist(supplier); + supplier.account(function(err, act) { + should.not.exist(e); + should.exist(act); + act.supplierName.should.equal('Supplier A'); + done(); + }); + }); + }); + + it('should destroy the related item on scope', function(done) { + Supplier.findById(supplierId, function(e, supplier) { + should.not.exist(e); + should.exist(supplier); + supplier.account.destroy(function(err) { + should.not.exist(e); + done(); + }); + }); + }); + + it('should get the related item on scope - verify', function(done) { + Supplier.findById(supplierId, function(e, supplier) { + should.not.exist(e); + should.exist(supplier); + supplier.account(function(err, act) { + should.not.exist(e); + should.not.exist(act); + done(); + }); + }); + }); + + it('should have deleted related item', function(done) { + Supplier.findById(supplierId, function (e, supplier) { + should.not.exist(e); + should.exist(supplier); + done(); + }); + }); + }); describe('hasAndBelongsToMany', function () {