Sequelize Error: Include unexpected. Element has to be either a Model, an Association or an object - mysql

I am receiving the error when I make a call to my API with a get request:
Include unexpected. Element has to be either a Model, an Association or an object.
My Models look like this:
module.exports = (sequelize, Sequelize) => {
const Productions = sequelize.define("productions", {
id: {
type: Sequelize.SMALLINT,
autoIncrement: true,
primaryKey: true
},
setupTime: {
type: Sequelize.DECIMAL(6, 3)
},
notes: {
type: Sequelize.TEXT
}
}, { timestamps: false });
return Productions;
};
module.exports = (sequelize, Sequelize) => {
const ProductionPrints = sequelize.define("productionPrints", {
id: {
type: Sequelize.SMALLINT,
autoIncrement: true,
primaryKey: true
},
compDate: {
type: Sequelize.DATE
}
}, { timestamps: false });
return ProductionPrints;
};
The relationship between the models is defined here:
db.productions = require("./productions.model.js")(sequelize, Sequelize);
db.productionprints = require("./production-prints.model.js")(sequelize, Sequelize);
db.productions.hasOne(db.productionprints, {
foreignKey: {
name: 'productionId',
allowNull: false
}
});
db.productionprints.belongsTo(db.productions, { foreignKey: 'productionId' });
And the sequelize query looks as so:
const db = require("../models");
const Productions = db.productions;
const ProductionPrints = db.productionPrints;
exports.findAll = (req, res) => {
Productions.findAll({
include: [ { model: ProductionPrints, as: 'prints' } ]
})
.then(data => {
res.send(data);
})
.catch(err => {
res.status(500).send({
message:
err.message || "An error occurred while finding the productions."
});
});
};
I have checked around for others with the issue but have had no avail with any solutions posted on those problems. Generally it was caused by typos, or error in the require paths. I have checked those and all my other includes work, just not on any of the models I include on the productions model.
Any feedback is greatly appreciated.

Error was being caused by a typo:
db.productions = require("./productions.model.js")(sequelize, Sequelize);
db.productionprints = require("./production-prints.model.js")(sequelize, Sequelize);
when this was being referenced in the assigned to a constant:
const Productions = db.productions;
const ProductionPrints = db.productionPrints;
shame on me for changing my case use:
db.productionprints != db.productionPrints

I had the same issue , this is usually caused by naming issue , to track the issue you can check one of the following places to resolve it
check if you are calling the correct model class name
when importing models becarefull not to call the file name instead of model name => the one exported
3.check if you got your association correctly by calling the exported model not the file name
check if your cases e.g users vs Users.
a bonus tip is to use same name for model and file name to avoid these issues because the moment you make them different you likely to make these mistakes

Following the answer of Kelvin Nyadzayo, i have the model.findOne(options) method with a
options.include like this:include: [ { } ] in the options parameter
The include has to have the proper syntax: [{model: Model, as: 'assciationName'}]
And the mine was empty
So this, was triggering the same error

Related

Sequelize error "Unknown column in 'field list'" during CREATE for One-to-Many Association

I have been using the following tutorial to learn how to implement one-to-many relationship in Sequelize where a Tutorial has many Comment and Comment belongs to a Tutorial: https://www.bezkoder.com/sequelize-associate-one-to-many/
While I have the code modeling what is documented for setting up the relationship between the two models, I receive the following error during creating the Comment model:
Unknown column 'tutorialId' in 'field list'
Additionally, I receive the following SQL output:
Executing (default): INSERT INTO `comments` (`id`,`name`,`text`,`createdAt`,`updatedAt`,`tutorialId`) VALUES (DEFAULT,?,?,?,?,?);
app.js
const tutorialsRouter = require('./routes/api/tutorials');
const commentsRouter = require('./routes/api/comments');
app.use('/api/tutorials', tutorialsRouter);
tutorialsRouter.use('/:tutorialId/comments', commentsRouter);
/models/index.js
db.tutorials = require("./tutorial.model")(sequelize, Sequelize);
db.comments = require("./comment.model")(sequelize, Sequelize);
db.tutorials.hasMany(db.comments, { as: "comments" });
db.comments.belongsTo(db.tutorials, {
foreignKey: "tutorialId",
as: "tutorial",
});
/models/comment.model.js
module.exports = (sequelize, DataTypes) => {
const Comment = sequelize.define('comment', {
name: {
type: DataTypes.STRING,
},
text: {
type: DataTypes.STRING,
},
});
return Comment;
}
/routes/comments.js
const comments = require('../../controllers/comments.controller');
const router = require('express').Router({ mergeParams: true });
router.post('/', comments.create);
module.exports = router;
/controllers/comments.controller.js
const db = require('../models');
const Comment = db.comments;
exports.create = (req, res) => {
...
Comment.create({
name: req.body.name,
text: req.body.text,
tutorialId: req.params.tutorialId,
})
.then( ... )
.catch( ... );
}
Then in Postman I receive a 500 (of the error message above) when issuing the request:
POST localhost:3000/api/tutorials/1/comments
{
"name": "John Doe",
"text": "Lorem ipsum..."
}
I don't think I should have to define a tutorialId field on the Comment model. Grr...
This may be very obvious to some of you, but it's tripping me up trying to learn. Any help is very much appreciated. :)
The issue that you're having is a result of using aliases via the as property. See the docs for belongsTo and hasMany. Here's a code sample that performs the inserts without the error.
let {
Sequelize,
DataTypes,
} = require('sequelize')
async function run () {
let sequelize = new Sequelize('dbname', 'username', 'password', {
host: 'localhost',
port: 5555,
dialect: 'postgres',
logging: console.log
})
let Comment = sequelize.define('comment', {
name: {
type: DataTypes.STRING,
},
text: {
type: DataTypes.STRING,
},
})
let Tutorial = sequelize.define('tutorial', {
title: {
type: DataTypes.STRING,
},
content: {
type: DataTypes.STRING,
}
})
Tutorial.hasMany(Comment)
Comment.belongsTo(Tutorial)
// This just recreates the tables in the database.
// You would really only want to use a force sync
// in a development environment, since it will destroy
// all of the data....
await sequelize.sync({ force: true })
let tutorial = await Tutorial.create({
title: 'Tutorial',
content: 'Hmm....'
})
let comment = await Comment.create({
name: 'Comment',
text: 'Something, something....',
tutorialId: tutorial.id,
})
await sequelize.close()
}
run()
Edit
This is just an edit to my original answer above. The OP Tom Doe discovered that the issue was being caused by a mismatch between the definitions of the tables in the database and the models defined via sequelize (see comments below). As we discovered, one way to troubleshoot the mismatch is to force sync a new version of the database, and then compare the new version of the database with the original version. There may be differences in the definitions of the columns or the contraints. Force syncing the database can be done via the command
await sequelize.sync({ force: true})
Important Note: The above statement will overwrite the existing database and all of its data. See the docs for more information.

Express.js and MySQL model + validation

I am developing application using Node.js and Express framework. I found many examples of modeling data using MongoDB, but my project requires SQL database.
Could someone make simple explanation, what is the best way to make models based on MySQL?
Also I am wondering how to provide later validation of those models. Maybe I should define validation attributes inside each of them?
There is no best way to make models based on MySQL. You could implement your own way to handle models, but there are many ORM modules available for Node.js, I'd suggest using one of those.
I use Sequelize as ORM to define models and interact with the database in several Express applications. Another ORM for Node that I've run into is Bookshelf.js, but there are many others. Wich one to use depends on your preferences and necessities.
EDIT: Example of usage
I suggest the following structure when using Sequelize models: a directory in your project named models with a file for each model and an index.js file to load the Sequelize environment. If you use the Sequelize CLI, it also has several methods that follow this structure.
index.js
const fs = require("fs");
const path = require("path");
const Sequelize = require("sequelize");
let sqize = new Sequelize({
host : "1.2.3.4",
port : 1234,
database : "testDb",
username : "pino",
password : "th1S1s#c0mpL3xP4sSw0rD",
dialect: 'mysql',
});
fs.readdirSync(__dirname).filter(function(file) {
return (file.indexOf(".") !== 0) && (file !== "index.js");
}).forEach(function(file) {
let model = sequelize.import(path.join(__dirname, file));
db[model.name] = model;
});
Object.keys(db).forEach(function(modelName) {
if ("associate" in db[modelName]) {
db[modelName].associate(db);
}
});
db.sequelize = sequelize;
db.Sequelize = Sequelize;
db.op = Sequelize.Op;
module.exports = {
sqize: sqize,
Sequelize: Sequelize,
op: Sequelize.Op
};
users.js
module.exports = function (sequelize, DataTypes) {
let users = sequelize.define('users', {
username: {
type: DataTypes.STRING(255),
allowNull: true
},
firstname: {
type: DataTypes.STRING(255),
allowNull: true
},
secondname: {
type: DataTypes.STRING(255),
allowNull: true
},
email: {
type: DataTypes.STRING(255),
allowNull: true
},
type: {
type: DataTypes.INTEGER(4),
allowNull: true,
references: {
model: 'users_type',
key: 'id'
}
},
password: {
type: DataTypes.STRING(255),
allowNull: true
},
salt: {
type: DataTypes.STRING(255),
allowNull: true
}
}, {
tableName: 'users'
});
users.associate = function (models) {
users.belongsTo(models.user_types, {
foreignKey: "type",
as: "userType"
});
users.hasMany(models.user_logs, {
foreignKey: "user_id",
as: "userLogs"
});
};
return users;
};
For more parameters and details, you can check the Sequelize doc, which is very simple and full of examples and details.
Also, I've used some ECMAScript 6, so change or transpile this code if your version of Node.js does not support them.

Unique email address with Sequelize

I'm running ExpressJS with Sequelize/MySQL and trying very hard to get a simple validator check working for unique email address.
Here is my user model. And for the life of me I don't understand why this is allowing records that have duplicate email address. Surely the email.unique=true would be preventing this.
'use strict';
module.exports = (sequelize, DataTypes) => {
var User = sequelize.define('User', {
firstName: DataTypes.STRING,
lastName: DataTypes.STRING,
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
validate: {
isEmail: {
msg: "Must be a valid email address",
}
}
}
}, {
indexes: [{
fields: ['email'],
unique: true,
}]
});
return User;
};
Any help appreciated.
EDIT:
As requested here is the controller code for create user.
const User = require('../models').User;
exports.create = (req, res) => {
User.create( req.body )
.then( user => {
res.json( user );
})
.catch( errors => {
res.json({ errors: errors.errors });
});
};
One way to solve this is by using sequelize.sync() to create your table according to the schema specified in the model if the table exists then you should pass {force: true} to the sync method, the table will be dropped and a new one will be created.
though using sequelize.sync() is not highly recommended especially in production due to issues with migration files etc, you can google than for more details.

Bookshelf cascade delete

I have two models Company and CompanyAdmin. A model Company has many CompanyAdmins. I am trying to delete CompanyAdmins when a parent Company is deleted, using bookshelf plugin bookshelf-cascade-delete. I also use knex to connect mysql db. Here are my models:
const db = bookshelf(connection);
db.plugin(cascadeDelete);
const Company = db.Model.extend({
tableName: 'company',
idAttribute: 'id',
hasTimestamps: true,
company_admins: function () { return this.hasMany(CompanyAdmin); }, // console.log(this);
}, {
dependents: ['company_admins'],
});
const CompanyAdmin = db.Model.extend({
tableName: 'company_admin',
idAttribute: 'id',
hasTimestamps: true,
company: function () { return this.belongsTo(Company) },
});
When I console.log(this) in company_admins function, I get this data:
ModelBase {
tableName: 'company',
idAttribute: 'id',
hasTimestamps: true,
company_admins: [Function: company_admins] }
Here is my DELETE route handler:
.delete((req, res) => {
if (req.user) {
Company.forge({
id: req.user.attributes.company_id,
})
.destroy()
.then(() => {
res.status(200)
.json({
status: 200,
error: false,
});
req.logout();
}).catch((err) => {
console.log(err);
res.status(500)
.json({
status: 500,
error: err.message,
});
});
} else {
res.status(401)
.json({
status: 401,
message: 'User not authenticated',
});
}
});
I am getting this error:
TypeError: Cannot read property 'hasMany' of undefined
Anybody with the same problem?
I solved this. Error was in importing bookshelf.
The code I was using before was:
import bookshelf from 'bookshelf';
const db = bookshelf(connection);
And the correct import of bookshelf look like this:
const db = require('bookshelf')(connection);
Not quite sure why the previous code didn't work, but the modified version works well!

Sequelize hook afterUpdate How to get dirty fields, original value and updated value?

I'm stuck with sequelize hooks, trying to write every change to a model into a log table. Therefore I'm looking for a way to access the models data before and after the write to MySQL.
How can I access this data within Sequelize Hook afterUpdate?
How can I get the updated/changed/dirty fields?
How can I access data before and after the update to make diffs?
Hook functions first argument is instance. As long as instance is fetched ahead of update operation, instance._previousDataValues and instance._change are available.
sequelize.addHook(
"afterCreate",
(i) => {
console.log(i);
}
);
module.exports = (sequelize, DataTypes) => {
const ContractLineItem = sequelize.define('ContractLineItem', {
id: {
type: DataTypes.INTEGER,
field: 'id',
allowNull: false,
primaryKey: true,
autoIncrement: true
}
//more attributes here
}, {
schema: 'public',
tableName: 'ContractLineItem',
timestamps: true
});
ContractLineItem.beforeBulkUpdate((contractLineItem, options) => {
console.log("b4 update contractLineItem....contractLineItem._change =" + contractLineItem._change);
if (!contractLineItem._change){
console.log("nothing changed.............");
//skip updating record
//return next();
}else{
console.log("something changed................");
//update record
}
console.log("after checking on change....");
});
ContractLineItem.associate = (models) => {
ContractLineItem.belongsTo(models.Contract, {
foreignKey: 'contractId',
});
//more relationships
};
return ContractLineItem;
};
Use instance.dataValues and instance._previousDataValues to access values after and before object update. This is valid for instance hooks, not bulk hooks. taken from Sequelize Hooks- Previous data values for afterBulkUpdate