Sails.js 중첩 모델
문제
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" 속성을 인식하지 못합니다.Sais가 중첩된 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"이라는 속성이 있다는 것을 알고 있지만 이것이 이 모듈에 어떻게 맞는지는 잘 모르겠습니다.
해결책
Waterline은 중첩 스키마 정의를 지원하지 않지만 다음을 사용할 수 있습니다. json
모델에 포함된 객체를 저장하려면 유형을 입력하세요.따라서 다음을 수행합니다.
profile: {
type: 'json',
required: true
}
그런 다음 다음과 같은 User 인스턴스를 만들 수 있습니다.
User.create({profile: {firstName: 'John', lastName: 'Doe'}})
차이점은 firstName
그리고 lastName
필드의 유효성이 검사되지 않습니다.포함된 스키마가 유효한지 확인하려는 경우 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();
}
}
다른 팁
수행원 @sgress454 답변 이 문제를 해결하기 위해 몇 가지 추가 논리를 만들었습니다.
나는 이 "중첩 모델"을 두 번 이상 사용하고 있으므로 이를 위한 팩토리를 갖춘 서비스를 만들었습니다. 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)
};
아직 완벽하지 않다는 것을 알고 있습니다. 아마도 이것을 후크로 만들어야 할 수도 있지만 그렇게 하는 가장 좋은 방법은 여전히 혼란스럽습니다.