sails.js modelos anidados
Pregunta
En sails.js 0.10 estoy tratando de hacer lo siguiente
// user.js
module.exports = {
attributes: {
uuid: {
type: 'string',
primaryKey: true,
required: true
} ,
profile: {
firstname: 'string',
lastname: 'string',
birthdate: 'date',
required: true
}
}
};
Estoy recibiendo un mensaje de error al intentar crear un usuario y sailsJS no reconoce el "perfil" de atributo.No estoy seguro de si las velas apoya la anidados estructura JSON, y si no no estoy seguro de cómo estructurarlo.
error: Sent 500 ("Server Error") response
error: Error: Unknown rule: firstname
He intentado lo siguiente pero no demasiado
// user.js
module.exports = {
attributes: {
uuid: {
type: 'string',
primaryKey: true,
required: true
} ,
profile: {
firstname: {type: 'string'},
lastname: {type: 'string'},
birthdate: 'date',
required: true
}
}
};
Sé que hay un atributo llamado "JSON" con sailsJS 0.10, pero no sabe cómo que se ajuste a este módulo.
Solución
De la línea de flotación no admite la definición de anidado de los esquemas, pero usted puede utilizar el json
tipo de almacén de objetos incrustados en su modelo.Por lo tanto, podría hacer:
profile: {
type: 'json',
required: true
}
Y, a continuación, puede crear instancias de Usuario como:
User.create({profile: {firstName: 'John', lastName: 'Doe'}})
la diferencia es que el firstName
y lastName
los campos no serán validados.Si desea validar que el esquema de la incrustados profile
objeto coincide con lo que usted quiere, usted tendrá que implementar el beforeValidate()
ciclo de vida de devolución de llamada en tu clase de modelo:
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();
}
}
Otros consejos
Siguiendo @ Sgress454 Respuesta He creado una lógica adicional para ayudar con este problema.
Dado que estoy usando estos "modelos anidados" más de una vez, creé un servicio con una fábrica para las devoluciones de llamadas de calefacción de ciclo de vida.
Aquí, cómo se ve:
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>
y, en el modelo, tengo esto:
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)
};
Sé que aún no es perfecto, tal vez debería hacer esto un gancho, pero todavía estoy confundido con la mejor manera de hacerlo.