简体   繁体   English

水线/帆Js防止模型的嵌套(关系)保存

[英]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 User.js

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

Subject.js 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: 当我为用户“ / user”调用蓝图创建函数并传递数据时:

{
    "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. 我尝试添加一个策略(如下所示),但这只会阻止使用URL“ / subject”创建主题,而不会阻止上面显示的嵌套创建。

'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. 如您所见,它甚至在验证或创建用户之前就正在验证和创建主题。

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 ),并把它添加到policies.js文件:

// 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',
  }

};

You can create custom type for subject, and add your logic inside model. 您可以为主题创建自定义类型,然后在模型中添加逻辑。 I'm not 100% sure I understood the attach sometimes part but maybe this could help: 我不是100%肯定有时会理解附加内容,但这可能会有所帮助:

models/User.js 模型/User.js

module.exports = {

    schema: true,

    attributes: {
        name: {
            type: 'string'
        },

        subject: {
            type: 'json',
            myValidation: true
        }
    },

    types: {
        myValidation: function(value) {
            // add here any kind of logic...

            // for example... reject if someone passed name key
            return !value.name;
        }
    }
};

You can find more info here http://sailsjs.org/documentation/concepts/models-and-orm/validations at the bottom of the page. 您可以在页面底部的http://sailsjs.org/documentation/concepts/models-and-orm/validations中找到更多信息。

If I totally missed the point... The second option would be to add beforeCreate and beforeUpdate lifecycle callback to your model like this: 如果我完全不了解这一点,那么第二个选择是像这样向模型添加beforeCreate和beforeUpdate生命周期回调:

models/User.js 模型/User.js

 module.exports = {

    schema: true,

    attributes: {
        name: {
            type: 'string'
        },

        subject: {
            type: 'json'
        }
    },

    beforeCreate: function (values, cb) {

        // for example... reject creating of subject if anything else then value of 1
        if (values.subject && values.subject !== 1) return cb('make error obj...');

        cb();
    },


    beforeUpdate: function (values, cb) {
        // here you can add any kind of logic to check existing user or current update values that are going to be updated
        // and allow it or not

        return cb();
    }
};

By using this you can use one logic for creating and another one for updating... etc... 通过使用此功能,您可以使用一种逻辑来创建,另一种逻辑来更新...等等...

You can find more info here: http://sailsjs.org/documentation/concepts/models-and-orm/lifecycle-callbacks 您可以在此处找到更多信息: http : //sailsjs.org/documentation/concepts/models-and-orm/lifecycle-callbacks

EDIT 编辑

Realized you have trouble with relation, and in above examples I thought you are handling type json... 意识到您在关系方面遇到麻烦,在上面的示例中,我认为您正在处理json类型...

module.exports = {

    schema: true,

    attributes: {
        name: {
            type: 'string'
        },

        subject: {
            model: 'subject'
        }
    },

    beforeValidate: function (values, cb) {

        // subject is not sent at all, so we just go to next lifecycle
        if (!values.subject) return cb();

        // before we update or create... we will check if subject by id exists...
        Subject.findOne(values.subject).exec(function (err, subject) {
            // subject is not existing, return an error
            if (err || !subject) return cb(err || 'no subject');

            //

            // you can also remove subject key instead of sending error like this:
            // delete values.subject;

            //

            // subject is existing... continue with update
            cb();
        });
    }
};

You should be passing the subject id (eg 1 ) instead of an object (eg { name: 'Hello, World!' } ) containing the name of the subject as it's not necessarily unique. 您应该传递主题ID(例如1 )而不是包含主题{ name: 'Hello, World!' }的对象(例如{ name: 'Hello, World!' } ),因为它不一定是唯一的。

If it is unique, you should replace the object by its id inside a beforeValidate for example. 如果它是唯一的,则应在例如beforeValidate中用其ID替换该对象。

// User.js

module.exports = {

  ...

  beforeValidate: function (users, callback) {
    // users = [{
    //   "name":"Test",
    //   "subject":{"name":"Do Not Allow"}
    // }]

    async.each(users, function replaceSubject(user, next) {
      var where = {};
      if (_.isObject(user.subject) && _.isString(user.subject.name)) {
        where.name = user.subject.name;
      } else if(_.isInteger(user.subject)) {
        where.id = user.subject;
      } else {
        return next();
      }

      // Check the existence of the subject
      Subject
        .findOne(where)
        .exec(function (err, subject) {
          if (err) return next(err);

          // Create a user without a subject if it does not exist
          user.subject = subject? subject.id : null;
          next();
      });
    }, callback);

    // users = [{
    //   "name":"Test",
    //   "subject":1
    // }]
  }

};

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM