Glen Robson Glen Robson - 5 months ago 57
Javascript Question

Waterline/SailsJs prevent nested (relationship) save of model

I have a model "User" that has a Many-to-One relationship with a "Subject".

User.js

attributes: {
subject: { model: 'subject' },
}


Subject.js

attributes: {
name: { type: 'string', unique: true, required: true },
}


When I call the blueprint create function for a User "/user" and pass in the data:

{
"name":"Test",
"subject":{"name":"Do Not Allow"}
}


It creates the user and also creates the Subject. However I do not want to allow the subject to be created, I only want to be able to attach an existing one. For example I would like it to reject the subject being created using the above data but allow the subject to be attached by using the below data.

{
"name":"Test",
"subject":1
}


I tried adding a policy (shown below) but this only stops the subject from being created using the URL "/subject" and not the nested create shown above.

'SubjectController':{
'create':false
}


Edit
To help understand what is going on here this is the lifecycle process it is going through:

Before Validation of Subject
After Validation of Subject
Before Creating Subject
After Creating Subject
Before Validation of User
After Validation of User
Before Creating User
Before Validation of User
After Validation of User
After Creating User


As you can see it is validating and creating the subject before it even gets to validating or creating the user.

Answer

You want to avoid the creation of an associated object when calling the blueprint creation route.

Create a policy (I've named it checkSubjectAndHydrate) and add it into the policies.js file:

// checkSubjectAndHydrate.js
module.exports = function (req, res, next) {

  // We can create a user without a subject
  if (_.isUndefined(req.body.subject)) {
    return next();
  }

  // Check that the subject exists
  Subject
    .findOne(req.body.subject)
    .exec(function (err, subject) {
      if (err) return next(err);

      // The subject does not exist, send an error message
      if (!subject) return res.forbidden('You are not allowed to do that');

      // The subject does exist, replace the body param with its id
      req.body.subject = subject.id;

      return next();
  });

};

// policies.js
module.exports.policies = {

  UserController: {
    create: 'checkSubjectAndHydrate',
    update: 'checkSubjectAndHydrate',
  }

};