【问题标题】:sails.js nested modelsSails.js 嵌套模型
【发布时间】:2014-07-24 01:51:28
【问题描述】:

在sails.js 0.10 中,我正在尝试执行以下操作

// user.js
module.exports = {
  attributes: {

    uuid: {
        type: 'string',
        primaryKey: true,
        required: true
     } ,
     profile: {

        firstname: 'string',
        lastname: 'string',
        birthdate: 'date',
        required: true
     }
  }
};

我在尝试创建用户时遇到错误,并且sailsJS 无法识别“profile”属性。我不确定sails 是否支持嵌套的JSON 结构,如果支持,我不确定如何构建它。

error: Sent 500 ("Server Error") response
error: Error: Unknown rule: firstname

我尝试了以下方法,但也失败了

// user.js
module.exports = {
  attributes: {

    uuid: {
        type: 'string',
        primaryKey: true,
        required: true
     } ,
     profile: {

        firstname: {type: 'string'},
        lastname: {type: 'string'},
        birthdate: 'date',
        required: true
     }
  }
};

我知道sailsJS 0.10 有一个名为“JSON”的属性,但不确定它是否适合这个模块。

【问题讨论】:

    标签: node.js sails.js waterline


    【解决方案1】:

    Waterline 不支持定义嵌套模式,但您可以使用 json 类型在模型中存储嵌入对象。所以,你会这样做:

    profile: {
        type: 'json',
        required: true
    }
    

    然后你可以创建像这样的用户实例:

    User.create({profile: {firstName: 'John', lastName: 'Doe'}})
    

    区别在于firstNamelastName 字段不会被验证。如果您想验证嵌入的 profile 对象的架构是否与您想要的匹配,您必须在模型类中实现 beforeValidate() 生命周期回调:

    attributes: {},
    beforeValidate: function(values, cb) {
        // If a profile is being saved to the user...
        if (values.profile) {
           // Validate that the values.profile data matches your desired schema,
           // and if not call cb('profile is no good');
           // otherwise call cb();
        }
    }
    

    【讨论】:

    • 谢谢!这真的很有帮助
    【解决方案2】:

    @sgress454 answer 之后,我创建了一些额外的逻辑来帮助解决这个问题。

    由于我不止一次使用这种“嵌套模型”,因此我为那些 beforeValidade() 生命周期回调创建了一个带有工厂的服务。


    如下所示:

    var _ = sails.util;
    var WLValidationError = require('../../node_modules/sails/node_modules/waterline/lib/waterline/error/WLValidationError.js');
    
    
    function validationError(invalidAttributes, status, message) {
      // Wrapper to helo with validation Errors
      return new WLValidationError({
          invalidAttributes: invalidAttributes,
          status: status,
          message: message
        }
      );
    }
    
    
    function fulfillJSON(attrValues, innerName, innerAttr) {
      // Helper to get default values into the JSON
      // Work with recurrency for arrays
      if (_.isArray(attrValues)) {
        return attrValues.map((attrVal) => {
          return fulfillJSON(attrVal, innerName, innerAttr);
        });
      }
    
      innerValue = attrValues[innerName];
      // Treat empty values
      if (innerValue == null) {
        // Check to see if it's required
        if (!innerAttr.required) {
          // If not required, try to set the defult value
          innerValue = innerAttr.defaultsTo;
        }
      }
    
      return attrValues;
    }
    
    
    function validateJSON(attrValues, innerName, innerAttr, invalidAttr, index) {
      // Helper to get error messages if it's not valid
      // Work with recurrency for arrays
      if (_.isArray(attrValues)) {
        invalidAttr = invalidAttr || {};
        _.each(attrValues, (attrVal) => {
          invalidAttr = validateJSON(attrVal, innerName, innerAttr, invalidAttr, attrValues.indexOf(attrVal));
        });
        return invalidAttr;
      }
    
      invalidMessage = "";
    
      innerValue = attrValues[innerName];
      // Treat empty values
      if (innerValue == null) {
        // Check to see if it's required
        if (innerAttr.required) {
          invalidMessage += '\n`' + innerName + '` is required!'
        };
      } else
      // Check if it has the right data type
      if (innerAttr.type) {
        if (typeof innerValue !== innerAttr.type) {
          invalidMessage += '\n`' + innerName + '` should be of type `' + innerAttr.type + '`!'
        };
      }
    
      if (invalidMessage != "") {
        invalidAttr = invalidAttr || {};
        innerInvalid = invalidAttr[innerName];
    
        if (innerInvalid != null && !_.isArray(innerInvalid)) {
          // Create an array if this attribute already have errors
          innerInvalid = [innerInvalid]
        };
        if (_.isArray(innerInvalid)) {
          // If it's an array, push new errors
          innerInvalid.push({
            index: index,
            field: innerName,
            value: innerValue,
            message: invalidMessage
          });
        } else {
          // If it's the first error, just create the object
          innerInvalid = {
            index: index,
            field: innerName,
            value: innerValue,
            message: invalidMessage
          };
        }
    
        invalidAttr[innerName] = innerInvalid;
      }
      return invalidAttr;
    }
    
    
    module.exports = {
      validateJSONFactory: function(jsonAttrs) {
    
        return function(values, cb) {
          // Object to store possible errors
          var invalidAttributes;
    
          // Go through each attibue trying to find json
          _.each(jsonAttrs, (attrSpecs, attrName) => {
            // Object to store specific attribute errors
            var invalidAttr;
    
            // Get the values to be validated
            attrValues = values[attrName]
            try {
              attrValues = JSON.parse(attrValues);
            } catch(e) {
              // console.log("Couldn't parse object, ignoring for now!")
              invalidAttributes[attrName] = {
                message: "Couldn't parse object!"
              };
              return false;
            }
    
            // Check if the specs are those of arrays
            if (_.isArray(attrSpecs)) {
              attrSpecs = attrSpecs[0];
              // Treat should be arrays
              if (!_.isArray(attrValues)) {
                attrValues = [attrValues];
              }
            }
    
            //Go through the specs in order to do some validation
            _.each(attrSpecs, (innerAttr, innerName) => {
              attrValues = fulfillJSON(attrValues, innerName, innerAttr);
              invalidAttr = validateJSON(attrValues, innerName, innerAttr, invalidAttr);
            });
    
            // Overload initial value, give back as string, the same way we got it!
            // values[attrName] = JSON.stringify(attrValues)
            values[attrName] = attrValues;
    
            // Make errors available outside
            if (invalidAttr != null){
              invalidAttributes = invalidAttributes || {};
              invalidAttributes[attrName] = invalidAttr;
            }
    
          }) // </each>
    
          if (invalidAttributes != null) {
            return cb(validationError(invalidAttributes));
          }
    
          return cb();
    
        } // </return function>
      } // </fulfillJSONFactory>
    }  // </module.exports>
    


    而且,在模型中,我有这个:

    const jsonAttrs = {
      profile: {
        // Here you can add some specifications for your nested attributes
        // If you wish, you can have a list of profiles by wrapping this inner object in an array
        firstName: {
          type: 'string',  // `type` will be used in a `typeOf` comparison
          required: true  // `required` will check if the value is present
          // defaultsTo: 'John'  -  is also an option and will bring this value if none is given
          // more options can be added here, you just need to implement some logic on the service
        },
        lastName: {
          type: 'string',
          required: true
        }
      }
    }
    
    
    module.exports = {
    
      attributes: ModelService.complete({
        profile: {
          // Note that you don't need anything in here
        }
      }),
    
      beforeValidate: ModelService.validateJSONFactory(jsonAttrs)
    
    };
    


    我知道它还不完美,也许我应该把它做成一个钩子,但我仍然对最好的方法感到困惑。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-06-03
      • 1970-01-01
      • 2013-09-09
      • 2014-10-24
      • 2014-06-20
      • 2017-07-06
      • 1970-01-01
      相关资源
      最近更新 更多