Make sure type of the foreign key match the primary key
See https://github.com/strongloop/loopback/issues/353
This commit is contained in:
parent
a1d3e72046
commit
4515491318
|
@ -1579,22 +1579,26 @@ DataSource.prototype.idProperty = function (modelName) {
|
||||||
* @param {String} foreignClassName The foreign model name
|
* @param {String} foreignClassName The foreign model name
|
||||||
*/
|
*/
|
||||||
DataSource.prototype.defineForeignKey = function defineForeignKey(className, key, foreignClassName) {
|
DataSource.prototype.defineForeignKey = function defineForeignKey(className, key, foreignClassName) {
|
||||||
// quit if key already defined
|
var pkType = null;
|
||||||
if (this.getModelDefinition(className).rawProperties[key]) return;
|
var foreignModel = this.getModelDefinition(foreignClassName);
|
||||||
|
var pkName = foreignModel && foreignModel.idName();
|
||||||
var defaultType = Number;
|
if (pkName) {
|
||||||
if (foreignClassName) {
|
pkType = foreignModel.properties[pkName].type;
|
||||||
var foreignModel = this.getModelDefinition(foreignClassName);
|
|
||||||
var pkName = foreignModel && foreignModel.idName();
|
|
||||||
if (pkName) {
|
|
||||||
defaultType = foreignModel.properties[pkName].type;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
var model = this.getModelDefinition(className);
|
||||||
|
if (model.properties[key]) {
|
||||||
|
if (pkType) {
|
||||||
|
// Reset the type of the foreign key
|
||||||
|
model.rawProperties[key].type = model.properties[key].type = pkType;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.connector.defineForeignKey) {
|
if (this.connector.defineForeignKey) {
|
||||||
var cb = function (err, keyType) {
|
var cb = function (err, keyType) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
// Add the foreign key property to the data source _models
|
// Add the foreign key property to the data source _models
|
||||||
this.defineProperty(className, key, {type: keyType || defaultType});
|
this.defineProperty(className, key, {type: keyType || pkType});
|
||||||
}.bind(this);
|
}.bind(this);
|
||||||
switch (this.connector.defineForeignKey.length) {
|
switch (this.connector.defineForeignKey.length) {
|
||||||
case 4:
|
case 4:
|
||||||
|
@ -1607,7 +1611,7 @@ DataSource.prototype.defineForeignKey = function defineForeignKey(className, key
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Add the foreign key property to the data source _models
|
// Add the foreign key property to the data source _models
|
||||||
this.defineProperty(className, key, {type: defaultType});
|
this.defineProperty(className, key, {type: pkType});
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -336,7 +336,7 @@ RelationDefinition.hasMany = function hasMany(modelFrom, modelTo, params) {
|
||||||
|
|
||||||
if (!params.through) {
|
if (!params.through) {
|
||||||
// obviously, modelTo should have attribute called `fk`
|
// obviously, modelTo should have attribute called `fk`
|
||||||
modelTo.dataSource.defineForeignKey(modelTo.modelName, fk, this.modelName);
|
modelTo.dataSource.defineForeignKey(modelTo.modelName, fk, modelFrom.modelName);
|
||||||
}
|
}
|
||||||
|
|
||||||
var scopeMethods = {
|
var scopeMethods = {
|
||||||
|
@ -348,6 +348,9 @@ RelationDefinition.hasMany = function hasMany(modelFrom, modelTo, params) {
|
||||||
scopeMethods.create = scopeMethod(definition, 'create');
|
scopeMethods.create = scopeMethod(definition, 'create');
|
||||||
scopeMethods.add = scopeMethod(definition, 'add');
|
scopeMethods.add = scopeMethod(definition, 'add');
|
||||||
scopeMethods.remove = scopeMethod(definition, 'remove');
|
scopeMethods.remove = scopeMethod(definition, 'remove');
|
||||||
|
} else {
|
||||||
|
scopeMethods.create = scopeMethod(definition, 'create');
|
||||||
|
scopeMethods.build = scopeMethod(definition, 'build');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mix the property and scoped methods into the prototype class
|
// Mix the property and scoped methods into the prototype class
|
||||||
|
@ -623,6 +626,11 @@ BelongsTo.prototype.create = function(targetModelData, cb) {
|
||||||
var pk = this.definition.keyFrom;
|
var pk = this.definition.keyFrom;
|
||||||
var modelInstance = this.modelInstance;
|
var modelInstance = this.modelInstance;
|
||||||
|
|
||||||
|
if (typeof targetModelData === 'function' && !cb) {
|
||||||
|
cb = targetModelData;
|
||||||
|
targetModelData = {};
|
||||||
|
}
|
||||||
|
|
||||||
modelTo.create(targetModelData, function(err, targetModel) {
|
modelTo.create(targetModelData, function(err, targetModel) {
|
||||||
if(!err) {
|
if(!err) {
|
||||||
modelInstance[fk] = targetModel[pk];
|
modelInstance[fk] = targetModel[pk];
|
||||||
|
@ -695,7 +703,7 @@ BelongsTo.prototype.related = function (refresh, params) {
|
||||||
return cachedValue;
|
return cachedValue;
|
||||||
}
|
}
|
||||||
} else if (params === undefined) { // acts as sync getter
|
} else if (params === undefined) { // acts as sync getter
|
||||||
return modelInstance[fk];
|
return cachedValue;
|
||||||
} else { // setter
|
} else { // setter
|
||||||
modelInstance[fk] = params;
|
modelInstance[fk] = params;
|
||||||
self.resetCache();
|
self.resetCache();
|
||||||
|
@ -813,33 +821,77 @@ RelationDefinition.hasOne = function (modelFrom, modelTo, params) {
|
||||||
* @param {String|Object} err Error string or object
|
* @param {String|Object} err Error string or object
|
||||||
* @param {Object} The newly created target model instance
|
* @param {Object} The newly created target model instance
|
||||||
*/
|
*/
|
||||||
HasOne.prototype.create = function(targetModelData, cb) {
|
HasOne.prototype.create = function (targetModelData, cb) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var modelTo = this.definition.modelTo;
|
var modelTo = this.definition.modelTo;
|
||||||
var fk = this.definition.keyTo;
|
var fk = this.definition.keyTo;
|
||||||
var pk = this.definition.keyFrom;
|
var pk = this.definition.keyFrom;
|
||||||
var modelInstance = this.modelInstance;
|
var modelInstance = this.modelInstance;
|
||||||
var relationName = this.definition.name
|
|
||||||
|
|
||||||
|
if (typeof targetModelData === 'function' && !cb) {
|
||||||
|
cb = targetModelData;
|
||||||
|
targetModelData = {};
|
||||||
|
}
|
||||||
|
targetModelData = targetModelData || {};
|
||||||
|
targetModelData[fk] = modelInstance[pk];
|
||||||
|
var query = {where: {}};
|
||||||
|
query.where[fk] = targetModelData[fk]
|
||||||
|
modelTo.findOne(query, function(err, result) {
|
||||||
|
if(err) {
|
||||||
|
cb(err);
|
||||||
|
} else if(result) {
|
||||||
|
cb(new Error('HasOne relation cannot create more than one instance of '
|
||||||
|
+ modelTo.modelName));
|
||||||
|
} else {
|
||||||
|
modelTo.create(targetModelData, function (err, targetModel) {
|
||||||
|
if (!err) {
|
||||||
|
// Refresh the cache
|
||||||
|
self.resetCache(targetModel);
|
||||||
|
cb && cb(err, targetModel);
|
||||||
|
} else {
|
||||||
|
cb && cb(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a target model instance
|
||||||
|
* @param {Object} targetModelData The target model data
|
||||||
|
* @callback {Function} [cb] Callback function
|
||||||
|
* @param {String|Object} err Error string or object
|
||||||
|
* @param {Object} The newly created target model instance
|
||||||
|
*/
|
||||||
|
HasMany.prototype.create = function (targetModelData, cb) {
|
||||||
|
var self = this;
|
||||||
|
var modelTo = this.definition.modelTo;
|
||||||
|
var fk = this.definition.keyTo;
|
||||||
|
var pk = this.definition.keyFrom;
|
||||||
|
var modelInstance = this.modelInstance;
|
||||||
|
|
||||||
|
if (typeof targetModelData === 'function' && !cb) {
|
||||||
|
cb = targetModelData;
|
||||||
|
targetModelData = {};
|
||||||
|
}
|
||||||
targetModelData = targetModelData || {};
|
targetModelData = targetModelData || {};
|
||||||
targetModelData[fk] = modelInstance[pk];
|
targetModelData[fk] = modelInstance[pk];
|
||||||
modelTo.create(targetModelData, function(err, targetModel) {
|
modelTo.create(targetModelData, function(err, targetModel) {
|
||||||
if(!err) {
|
if(!err) {
|
||||||
// Refresh the cache
|
// Refresh the cache
|
||||||
self.resetCache(targetModel);
|
self.addToCache(targetModel);
|
||||||
cb && cb(err, targetModel);
|
cb && cb(err, targetModel);
|
||||||
} else {
|
} else {
|
||||||
cb && cb(err);
|
cb && cb(err);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build a target model instance
|
* Build a target model instance
|
||||||
* @param {Object} targetModelData The target model data
|
* @param {Object} targetModelData The target model data
|
||||||
* @returns {Object} The newly built target model instance
|
* @returns {Object} The newly built target model instance
|
||||||
*/
|
*/
|
||||||
HasOne.prototype.build = function(targetModelData) {
|
HasMany.prototype.build = HasOne.prototype.build = function(targetModelData) {
|
||||||
var modelTo = this.definition.modelTo;
|
var modelTo = this.definition.modelTo;
|
||||||
var pk = this.definition.keyFrom;
|
var pk = this.definition.keyFrom;
|
||||||
var fk = this.definition.keyTo;
|
var fk = this.definition.keyTo;
|
||||||
|
@ -865,7 +917,6 @@ HasOne.prototype.related = function (refresh, params) {
|
||||||
var fk = this.definition.keyTo;
|
var fk = this.definition.keyTo;
|
||||||
var pk = this.definition.keyFrom;
|
var pk = this.definition.keyFrom;
|
||||||
var modelInstance = this.modelInstance;
|
var modelInstance = this.modelInstance;
|
||||||
var relationName = this.definition.name;
|
|
||||||
|
|
||||||
if (arguments.length === 1) {
|
if (arguments.length === 1) {
|
||||||
params = refresh;
|
params = refresh;
|
||||||
|
@ -907,7 +958,7 @@ HasOne.prototype.related = function (refresh, params) {
|
||||||
return cachedValue;
|
return cachedValue;
|
||||||
}
|
}
|
||||||
} else if (params === undefined) { // acts as sync getter
|
} else if (params === undefined) { // acts as sync getter
|
||||||
return modelInstance[pk];
|
return cachedValue;
|
||||||
} else { // setter
|
} else { // setter
|
||||||
params[fk] = modelInstance[pk];
|
params[fk] = modelInstance[pk];
|
||||||
self.resetCache();
|
self.resetCache();
|
||||||
|
|
|
@ -202,7 +202,9 @@ function defineScope(cls, targetClass, name, params, methods) {
|
||||||
var prop = targetClass.definition.properties[i];
|
var prop = targetClass.definition.properties[i];
|
||||||
if (prop) {
|
if (prop) {
|
||||||
var val = where[i];
|
var val = where[i];
|
||||||
if (typeof val !== 'object' || val instanceof prop.type) {
|
if (typeof val !== 'object' || val instanceof prop.type
|
||||||
|
|| prop.type.name === 'ObjectID') // MongoDB key
|
||||||
|
{
|
||||||
// Only pick the {propertyName: propertyValue}
|
// Only pick the {propertyName: propertyValue}
|
||||||
data[i] = where[i];
|
data[i] = where[i];
|
||||||
}
|
}
|
||||||
|
|
|
@ -155,7 +155,7 @@ describe('relations', function () {
|
||||||
should.not.exist(e);
|
should.not.exist(e);
|
||||||
should.exist(l);
|
should.exist(l);
|
||||||
l.should.be.an.instanceOf(List);
|
l.should.be.an.instanceOf(List);
|
||||||
todo.list().should.equal(l.id);
|
todo.list().id.should.equal(l.id);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -206,7 +206,7 @@ describe('relations', function () {
|
||||||
should.not.exist(e);
|
should.not.exist(e);
|
||||||
should.exist(act);
|
should.exist(act);
|
||||||
act.should.be.an.instanceOf(Account);
|
act.should.be.an.instanceOf(Account);
|
||||||
supplier.account().should.equal(act.id);
|
supplier.account().id.should.equal(act.id);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue