Sequelize associations not being created in MySQL, PostgreSQL and SQLite - mysql

I am defining associations in models using sequalize with MYSQL. But after migration, the foreign key is not being added to the target model as explained in sequelize docs.
I have also tried to manually define foreign keys in models and migration files but still no association is being created between tables. When I view the tables in relation view in PhpMyAdmin, not foreign key constraints or relationship is being created.
I have tried this with SQLite, and PostgreSQL with the same results. I don't know what I am doing wrong. Here are models.
AURHOR MODEL
//One author hasMany books
'use strict';
module.exports = (sequelize, DataTypes) => {
const Author = sequelize.define('Author', {
Name: DataTypes.STRING
}, {});
Author.associate = function(models) {
// associations can be defined here
Author.hasMany(models.Book)
};
return Author;
};
I expect sequelize to add authorId on books table as specified in the docs, but this not happening
BOOK MODEL
//Book belongs to Author
'use strict';
module.exports = (sequelize, DataTypes) => {
const Book = sequelize.define('Book', {
Title: DataTypes.STRING
}, {});
Book.associate = function(models) {
// associations can be defined here
Book.belongsTo(models.Author)
};
return Book;
};
No associations is being created between these two tables after migration.
I have as well tried to define custom foreign keys in model associations like this:
//Author model
Author.hasMany(models.Book,{foreignKey:'AuthorId'})
//Book model
Book.belongsTo(models.Author,{foreignKey:'AuthorId'})
still this not solving the problem
I have gone ahead to define foreign keys in models then referencing them in the association like this:
'use strict';
module.exports = (sequelize, DataTypes) => {
const Book = sequelize.define('Book', {
Title: DataTypes.STRING,
AuthorId:DataTypes.INTEGER
}, {});
Book.associate = function(models) {
// associations can be defined here
Book.belongsTo(models.Author,{foreignKey:'AuthorId'})
};
return Book;
};
But still no associations is being created
I finally decided to add references in migration files like so:
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Books', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
Title: {
type: Sequelize.STRING
},
AuthorId:{
type: Sequelize.INTEGER,
references:{
model:'Author',
key:'id'
}
}
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Books');
}
};
But when I run this kind of migration setup, I get this error: ERROR: Can't create table dbname.books (errno: 150 "Foreign key constraint is i
ncorrectly formed")
I get similar error when I switch to PostgreSQL.
I have been held back by this issue for quite long. What may I doing wrong. I am using sequelize version 4.31.2 with sequelize CLI.

I was referencing to models wrongly in migrations.
Wrong way
AuthorId:{
type: Sequelize.INTEGER,
references:{
model:'Author',
key:'id'
}
}
Correct Way
// Notes the model value is in lower case and plural just like the table name in the database
AuthorId:{
type: Sequelize.INTEGER,
references:{
**model:'authors',**
key:'id'
}
}
This solved my problem. The associations is now getting defined.

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.

Sequelize associations not generating foreign key

Sequelize is not creating the foreign key automatically, and is throwing a "no column "userId" in "fieldset"" error. I try to provide all the information down below. Im completely stuck on where to go from here as my code is 100% correct. (Read below)
So i have a Product and User model. both before were working fine. I added some code to set up the relationship:
Product.belongsTo(User, { constraints: true, onUpdate: "CASCADE" });
User.hasMany(Product);
I also, when syncing the db, have used {force: true} and removed it after tables were refreshed. Ive tried restarting pc after these steps, restarting workbench, creating a new database and changing connection to connect to fresh one, still it doesnt put a "userId" column in my product schema.
Ive had this code checked by two people so far and they confirm my syntax is fine, and are equally baffled. Im also confident myself that its not incorrect because im following a reputable course and i've now had to copy and paste his code in replacement to mine just incase, which didnt work.
Product model:
const Sequelize = require("sequelize");
const sequelize = require("../util/database");
const Product = sequelize.define("product", {
id: {
type: Sequelize.INTEGER,
autoIncrement: true,
allowNull: false,
primaryKey: true,
},
title: Sequelize.STRING,
price: {
type: Sequelize.DOUBLE,
allowNull: false,
},
image_url: {
type: Sequelize.STRING,
allowNull: false,
},
description: {
type: Sequelize.STRING,
allowNull: false,
},
});
module.exports = Product;
User model:
const Sequelize = require("sequelize");
const sequelize = require("../util/database");
const User = sequelize.define("user", {
id: {
type: Sequelize.INTEGER,
autoIncrement: true,
allowNull: false,
primaryKey: true,
},
name: Sequelize.STRING,
email: Sequelize.STRING,
});
module.exports = User;
Syncing code (I create a test user as the course is at a stage where we are testing we can make one):
// db.sync({ force: true })
db.sync()
.then((result) => {
return User.findByPk(1);
// console.log(result);
})
.then((user) => {
if (!user) {
return User.create({ name: "Max", email: "test#test.com" });
}
return user;
})
.then((user) => {
app.listen(5000);
})
.catch((err) => {
console.log(err);
});
The connection is 100% connected as things happen to the tables in my database, just the "userId" column which sequelize should auto-generate from my associations doesnt come up.
Also have tried putting in a foreignKey: "userId" in my Product.belongsTo() line of code to try to implicitly set it. That didnt even work.
Therefore im stuck and cannot continue with my sql code.
Github repo if need further code:
https://github.com/NinjaInShade/online-shop
I tried your code with some modifications about associations and foreign keys and you have two ways to create a column userId and a foreign key:
Add a userId field definition to Product model with references option like this:
userId: {
allowNull: true,
type: Sequelize.INTEGER,
references: {
model: 'users',
key: 'id'
}
}
Synchronize models individually using their sync method:
User.sync({ force: true })
.then(() => {
Product.sync({ force: true }).then(() => {
app.listen(5000);
})
})
Unfortunately the official documentation does not clarify why sync method in Sequelize acts differently in comparison with sync of separate models.
Usually I use migrations and that's why I don't have this issue.

Sequelize associate not creating foreign key

According to sequelize documentation, Model.hasMany should automatically create foreign key references with constraints. But when i try it in my code it does not. I have to manually add the foreign key to the migration. Please how can i fix this? This is my User model
'use strict';
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define('User', {
name: DataTypes.STRING,
username: DataTypes.STRING,
email: DataTypes.STRING,
password: DataTypes.STRING
});
User.associate = function(models) {
// associations can be defined here
User.hasMany(models.Post);
};
return User;
};
and this is my post model
'use strict';
module.exports = (sequelize, DataTypes) => {
const Post = sequelize.define('Post', {
title: DataTypes.STRING,
body: DataTypes.TEXT
});
Post.associate = function(models) {
Post.belongsTo(models.User);
};
return Post;
};
To create foreign key, you'll need to put the information of which column is the primary key and the foreign key referred:
'use strict';
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define('User', {
name: {
DataTypes.STRING,
primaryKey: true
},
username: DataTypes.STRING,
email: DataTypes.STRING,
password: DataTypes.STRING
});
User.associate = function(models) {
// associations can be defined here
User.hasMany(models.Post, {
foreignKey: modelId
);
};
return User;
};
Note that, in your User, I defined which column is the primary key and, in hasMany, I defined which column on Post is the foreignKey.
After much struggle with this I finally figured it out.
The db:migrate command uses the migration files and does not include the model files.
To use the model files instead, I just needed to do this in the app.js file
...
...
const db = require('./models');
db.sequelize.init();
Although a lot of experience programmers do not recommend this method. They suggest that the best method is manually creating the foreign key in the migration files.

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.

How to update migration files when change column or add new column using Sequelize

I'm new in Sequelize, right now I'm creating a RESTful api with NodeJS and Sequelize. I'm trying to figure out how to change my Database Schema like change my Column name using Sequelize
I create a Model like this
sequelize model:create --name MyUser --attributes first_name:string,last_name:string,bio:text
It created a file in Models
'use strict';
module.exports = function(sequelize, DataTypes) {
var Page = sequelize.define('Page', {
name: DataTypes.STRING,
text: DataTypes.TEXT,
url: DataTypes.STRING
}, {
classMethods: {
associate: function(models) {
// associations can be defined here
}
}
});
return Page;
};
and one file in Migrations Folder
'use strict';
module.exports = {
up: function(queryInterface, Sequelize) {
return queryInterface.createTable('Pages', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
name: {
type: Sequelize.STRING
},
text: {
type: Sequelize.TEXT
},
url: {
type: Sequelize.STRING
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: function(queryInterface, Sequelize) {
return queryInterface.dropTable('Pages');
}
};
The problem is how about if I want to add New column and change existing Column Name
Example I want to change to this
'use strict';
module.exports = function(sequelize, DataTypes) {
var Page = sequelize.define('Page', {
fullname: DataTypes.STRING,
text: DataTypes.TEXT,
url: DataTypes.STRING,
email: DataTypes.STRING
}, {
classMethods: {
associate: function(models) {
// associations can be defined here
}
}
});
return Page;
};
I have read a few page in Stackoverflow about this, like in this page
How to auto generate migrations with Sequelize CLI from Sequelize models?
and
Sequelize.js: how to use migrations and sync
One of that page has a way to Alter Column automatic using Sequelize-cmd in this link https://www.youtube.com/watch?v=wHTBxtk8ezo but Sequelize-cmd is already deprecated and the other way and the only way I do now is create Migration File using sequelize migration:createand manually write a code to rename and add Column using addColumn and renameColumn
So, my question now is there a way to Creating Migration File with addColumn and renameColumn Automatic like what Sequelize-cmd do without have to write it manually ?
First type sequelize migration:create --name changeColumn into your terminal after navigating to your project directory. This creates a new migration file in your migrations folder called changeColumn with the date of creation prepended.
Then you use the renameColumn method which takes in the table name, original column name and the new column name. Once you have updated the file to the below code, go back to your terminal and type sequelize db:migrate to run the migration.
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.renameColumn('Users',
'beforeName', 'afterName');
},
down: (queryInterface, Sequelize) => {
return queryInterface.renameColumn('Users', 'afterName', 'beforeName');
}
};
Simplest way to do this is
queryInterface.renameColumn('tableName', 'oldName', 'newName');
NOTE: I tries this in down function but not work it's working once I write this code in up function
There is a new npm package for solving this problem by comparing your models files and writing migration files for you
Sequelize-mig
Install it with:
npm install sequelize-mig -g / yarn global add sequelize-mig
then use it like this
sequelize-mig migration:make -n <migration name>
and it will auto generate the migration file for you with all updates read from your models files