I have model like
"properties": {
"id": {
"type": "string",
"id": true
},
"name": {
"type": "string"
},
"currency": {
"type": "string"
}
},
"dataSource": "mongodb",
"validations": [],
"relations": {
"user": {
"type": "belongsTo",
"model": "user",
"foreignKey": "userId",
"required": true
}
},
And user should be required for all records. But I can easilly create record without user. "required" is not working for related models.
May be I can create validator but I prefer native support.
Alex
There's currently no required option for relations, but it would certainly make sense to add one.
For now, the easiest validation you could implement would be on the foreignKey:
Product.validatesPresenceOf('userId');
Thank you
In the future, I'd like to implement the following:
"relations": {
"user": {
"type": "belongsTo",
"model": "user",
"required": true
},
"files": {
"type": "belongsTo",
"model": "user",
"minItems": 2,
"maxItems": 10
}
}
+1
I definitely agree that it would be more proper to have a "required" attribute on the relation object.
In the mean time though, you can add the foreign key as a property and set its "required" attribute to true. Using the example json you mentioned, it would look something like this:
"properties": {
"id": {
"type": "string",
"id": true
},
"name": {
"type": "string"
},
"currency": {
"type": "string"
},
"userId": {
"required": true
}
},
"dataSource": "mongodb",
"validations": [],
"relations": {
"user": {
"type": "belongsTo",
"model": "user",
"foreignKey": "userId"
}
},
Thanks. It is good solution for now. !
2014-10-20 13:59 GMT-04:00 mymainmanbrown [email protected]:
I definitely agree that it would be more proper to have a "required"
attribute on the relation object.In the mean time though, you can add the foreign key as a property and set
its "required" attribute to true. Using the example json you mentioned, it
would look something like this:"properties": {
"id": {
"type": "string",
"id": true
},
"name": {
"type": "string"
},
"currency": {
"type": "string"
},
"userId": {
"required": true
}
},
"dataSource": "mongodb",
"validations": [],
"relations": {
"user": {
"type": "belongsTo",
"model": "user",
"foreignKey": "userId"
}
},—
Reply to this email directly or view it on GitHub
https://github.com/strongloop/loopback/issues/505#issuecomment-59810811.
It's good~! thank you ~!
+1
+1
+1 thanks for the tips above
Thanks for these important hints.
However, unless I have misunderstood something, this trick (creating a required property that matches the name of the custom foreign key in a relation) only works if:
a) you also specify the type of the property - "any" will do, particularly if the id of the related model is an object identifier. If the type is omitted, the API explorer does not work at all.
b) the name of the id property in the related model is "id". This is crucial - none of the relationship code in LoopBack seems to work if you decide that you want a field called, for example, "name" to be the primary key. The only exception to this that I have discovered is that you can explicitly name the primary key field of a model called "size", for example, "sizeName" and then specify that as the custom foreign key.
Even then, I find that the API Explorer ignores the "include" filter for relations of type "hasOne", while it works for "belongsTo".
If I have misinterpreted something here, please correct me!
@immo-huneke-zuhlke, I'm seeing the same behaviour here. I think the most reliable solution is still the one proposed at the start, Product.validatesPresenceOf('userId');
+1
"relations": {
"address": {
"type": "embedsOne",
"model": "Address",
"property": "billingAddress",
}
I just tried with embedsOne and it validates that the relation must be defined.
Not sure that it's what you are looking for, but that's what I was looking for so I thought that I would share
+1
@superkhau : would you know when the "required" option on relations should land in a coming sprint?
@ebarault It's not high in the priority list ATM. Would someone like to submit a PR? I can get it bumped in priority for community contributions.
Two years and still missing?
@cojack We have many priorities in our backlog and this one is probably still low on the list. I will ping the people who can do something about it planning that can give you a better answer than me. @bajtos @0candy Can you chime in here?
Yes, this is not a priority for us right now. Having said that, if anyone decides to contribute this feature, then we are happy to help along the way.
+1 for require and also for:
"files": {
"type": "belongsTo",
"model": "user",
"**minItems**": 2,
"**maxItems**": 10
}
Just wanted to comment that @mymainmanbrown's trick above does not work (at least with the latter versions of Loopback). It will create a BLOB/TEXT column which is unsuitable for the foreign key constraint.
That is possible by creating and using mixins (sorry for the comments in French)
/common/mixins/BelongsToIntegrityCheck.js
/*Ce mixin permet de vérifier les contraintes d'intégrité (l'objet enfant possède bien un parent)
en vérifiant que les clés étrangères des objets parents existes bien lors de la création d'un objet enfant*/
'use strict'
let _ = require('lodash');
let checkBelongsToIntegrity = function (ctx, next) {
if (ctx.instance) {
let relations = ctx.Model.definition.settings.relations;
let relationsArray = _.map(relations, rel => {
return { modelName: rel.model, fk: rel.foreignKey, type: rel.type };
});
/* On utilise Lodash pour transformer l'objet des relations en Tableau de la forme
[
{ modelName: 'achat', fk: 'achat_id', type: 'belongsTo' },
{ modelName: 'FED_AGENT', fk: 'agent_rfagent', type: 'belongsTo' }
]
*/
let thisModel = ctx.Model;
// Le message qui sera renvoyé en cas d'échec de vérification des contraintes d'intégrité
let message = "";
// Le tableau des promises correspondant aux requêtes vérifiants les contraintes
let promiseArray = [];
relationsArray.forEach(function (relation) {
if (relation.type == 'belongsTo') {
let parentModelName = relation.modelName;
let parentModel = thisModel.app.models[parentModelName];
let parentId = ctx.instance[relation.fk];
// On cherche le modèle parent qui correspond à l'id demandé pour le modèle enfant...
promiseArray.push(parentModel.findById(parentId).then(function (parentInstance) {
if (parentInstance === null) {
message += 'No ' + parentModelName + ' with "' + parentId + '" id. ';
}
}));
}
}
);
/* Une fois que toutes les promesses ont été déterminées et conduisent vers un message en cas de non respect de la contrainte d'intégrité,
on les regroupe dans une promesse commune résolue quand toutes sont résolues et qui renvoit le message en cas de non respect de contrainte */
Promise.all(promiseArray)
.then(
function () {
next(message);
}
, console.error)
.catch(function (err) {
next(err);
});
}
}
module.exports = function (Model, options) {
Model.observe('before save', checkBelongsToIntegrity);
};
/common/mixins/HasManyIntegrityCheck.js
/*Ce mixin permet de vérifier les contraintes d'intégrité (si l'objet parent possède des enfants, on ne peut pas le supprimer)
en faisant une requête sur les enfants qui sont rattachés à ce parent*/
'use strict'
let _ = require('lodash');
let checkHasManyIntegrity = function (ctx, next) {
if (ctx.where) {
let relations = ctx.Model.definition.settings.relations;
let relationsArray = _.map(relations, rel => {
return { modelName: rel.model, fk: rel.foreignKey, type: rel.type };
});
/* On utilise Lodash pour transformer l'objet des relations en Tableau de la forme
[
{ modelName: 'achat', fk: 'achat_id', type: 'belongsTo' },
{ modelName: 'FED_AGENT', fk: 'agent_rfagent', type: 'belongsTo' }
]
*/
let thisModel = ctx.Model;
// Le message qui sera renvoyé en cas d'échec de vérification des contraintes d'intégrité
let message = "";
// Le tableau des promises correspondant aux requêtes vérifiants les contraintes
let promiseArray = [];
relationsArray.forEach(function (relation) {
if (relation.type == 'hasMany') {
let childrenModelName = relation.modelName;
let childrenModel = thisModel.app.models[childrenModelName];
let parentId = ctx.where.id;
let whereObject = {};
whereObject[relation.fk] = ctx.where.id;
// On cherche les enfants éventuels
promiseArray.push(childrenModel.find({
where: whereObject
}).then(function (data) {
if (data.length > 0) { // Si il y a des enfants, on renvoit une erreur
message += 'This "' + thisModel.modelName + '" has "' + childrenModelName + '", and can\'t be deleted. ';
}
}));
}
}
);
/* Une fois que toutes les promesses ont été déterminées et conduisent vers un message en cas de non respect de la contrainte d'intégrité,
on les regroupe dans une promesse commune résolue quand toutes sont résolues et qui renvoit le message en cas de non respect de contrainte */
Promise.all(promiseArray)
.then(
function () {
next(message);
}
, console.error)
.catch(function (err) {
next(err);
});
}
}
module.exports = function (Model, options) {
Model.observe('before delete', checkHasManyIntegrity);
};
And in your models :
Parent Model :
"mixins": {
"HasManyIntegrityCheck": {}
},
Children Model :
"mixins": {
"BelongsToIntegrityCheck": {}
},
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
Any action on this issue?
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
Any plans on this?
Without this feature it's easy to fall into non-consistency at the database level. Such checks just must have if we want to keep our databases integrity.
Keeping in mind that you don't have such foolproof on the framework side is a really pain. You can't be sure that your code do all things well when manipulating model objects. And such bugs are expensive, because they are logic bugs (can't be caught fast => more expensive then others). But with this feature they can be caught by developer without much efforts.
Definitely +1 to have support for this. This is very important thing and imho it should be done with much higher priority then now. Please consider about this. Thanks.
@nicolaspernoud I will ameliorate your mixins to take into consideration the optional belongsTo relation.
I would add to the model relation an attribute optional which indicates if the can't be null or not.
Example :
{
"name": "taxonomy",
"base": "PersistedModel",
"idInjection": true,
....
"relations": {
....
"parent": {
"type": "belongsTo",
"model": "taxonomy",
"foreignKey": "parentId",
"optional" : true
}
}
so in the BelongsToIntegrityCheck.js i would add the following
...
let relationsArray = _.map(relations, rel => {
return { modelName: rel.model, fk: rel.foreignKey, type: rel.type, optional: rel.optional };
});
...
let byPassValidation = false;
if(relation.optional == true)
{
if(parentId == null)
byPassValidation = true;
}
.....
if(!byPassValidation) {
promiseArray.push(parentModel.findById(parentId).then(function (parentInstance) {
if (parentInstance === null) {
message += 'No ' + parentModelName + ' with "' + parentId + '" id. ';
}
}));
}
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
This issue has been closed due to continued inactivity. Thank you for your understanding. If you believe this to be in error, please contact one of the code owners, listed in the CODEOWNERS file at the top-level of this repository.
Most helpful comment
I definitely agree that it would be more proper to have a "required" attribute on the relation object.
In the mean time though, you can add the foreign key as a property and set its "required" attribute to true. Using the example json you mentioned, it would look something like this: