Reputation: 21
I am trying to make a dynamic update function for my application that does two things,
-checks if the new values are different from the old ones to avoid updating and thus changing the updated_at timestamp for no reason if no value is actually being changed
-checks if one of the attributes the user wants to change is a foreign key, and then checks if the said user modification refers to an actual row from the referred to model or not in order to avoid getting the error
so I need a way to extract information about attributes from the model the dynamically, and more precisely I need to be able to check if an attribute is a foreign key, and if it is I need to find the name of the model it is referring to
Upvotes: 1
Views: 543
Reputation: 21
If anyone needs this, here is how i managed to do it :
///////////////////////////////////////////////////////////////////////
// Unique keys checks
const hasUniqueConflicts = async (model, parameters)=>{
const uniqueFields = getUniqueFields(model, Object.keys(parameters))
const returns = new Set()
const uniqueParameters = uniqueFields.map(field=>{return {[field]:parameters[field]}})
// Check if any of the values of the unique fields already exists
const results = await model.findAll({where:{[Op.or]:uniqueParameters}})
if(results.length){
for(result of results){
for (field of uniqueFields){
if(parameters[field] === result[field]) returns.add(field)
}
}
return Array.from(returns)
}
return false
}
///////////////////////////////////////////////////////////////////////
// Foreign keys checks
const hasForeignKeyProblems = async (model, parameters)=>{
const foreignKeys = getForeignKeys(model, Object.keys(parameters))
const returns = new Set()
for(field of foreignKeys){
const valid = referencedRowExists(model, field, parameters[field])
if (!valid) returns.add(field)
}
if (returns.size) return Array.from(returns)
return false
}
//////////////////////////////////////////////////////////////
const referencedRowExists = async (model, foreignKey, foreignKeyValue)=>{
if(foreignKeyValue === null && isNullable(model, foreignKey)) return true
const models = getModels(model)
const tableName = getReferencedTableName(model, foreignKey)
for (table in models){
if (models[table].tableName == tableName){
referenceExists = await rowExists(models[table], foreignKeyValue)
if (referenceExists) return true
break
}
}
return false
}
const isNullable = (model, fieldName) => model.rawAttributes[fieldName].allowNull
const IsUnique = (model, fieldName) => model.rawAttributes[fieldName].unique
const IsForeignKey = (model, fieldName) => model.rawAttributes[fieldName].references && true
const getModels = (model) => model.sequelize.models
const getUniqueFields = (model, fieldNames) => fieldNames.filter(field=>IsUnique(model, field))
const getForeignKeys = (model, fieldNames) => fieldNames.filter(field=>IsForeignKey(model, field))
const getReferencedTableName = (model, foreignKey) => model.rawAttributes[foreignKey].references.model
const rowExists = async (model, parameter) => Object.keys(parameter).length ? !!(await model.count({where: parameter})) : false
Upvotes: 1
Reputation: 878
Check this out... looks like you can just use a hook for the first part on all your models... https://github.com/sequelize/sequelize/issues/8762. And i agree with Ken on the part above, your database will already prevent you from doing that so trap that error and customize what you wanna send back, instead of adding a bunch of preventative code.. Obviously you have some way to identify what model is the intended target of your input so you shouldn't need to worry about accessing attribute data.
Upvotes: 0