I am trying to map sequelize one to many association by referring to sequelize documentation but I could not able to find a complete example to get it work.
I have a ClaimType model as follows
const Sequelize = require('sequelize');
module.exports = function (sequelize) {
const ClaimType = sequelize.define('claim_type', {
id: {
type: Sequelize.INTEGER,
autoIncrement: true,
primaryKey: true
},
name: {
type: Sequelize.STRING
}
}, {
timestamps: false,
freezeTableName: true
});
return ClaimType;
};
and MaxClaimAmount
const Sequelize = require('sequelize');
module.exports = function (sequelize) {
const MaxClaimAmount = sequelize.define('max_claim_amount', {
id: {
type: Sequelize.INTEGER,
autoIncrement: true,
primaryKey: true
},
amount: {
type: Sequelize.DECIMAL
},
year: {
type: Sequelize.INTEGER
}
}, {
timestamps: false,
freezeTableName: true
});
return MaxClaimAmount;
};
finally in index.js
const ClaimType = require('./model/claim_type.js')(sequelize);
const MaxClaimAmount = require('./model/max_claim_amount.js')(sequelize);
ClaimType.hasMany(MaxClaimAmount, { as: 'claimAmount' });
sequelize.sync({ force: false }).then(() => {
return sequelize.transaction(function (t) {
return ClaimType.create({
name: 'OPD'
}, { transaction: t }).then(function (claimType) {
// want to know how to associate MaxClaimAmount
});
}).then(function (result) {
sequelize.close();
console.log(result);
}).catch(function (err) {
sequelize.close();
console.log(err);
});
});
ClaimType object is returned from the first part of the transaction and I want to know how to implement the association between ClaimType and MaxClaimAmount?
You make the association in your models.
(Associations: one-to-many in sequelize docs)
const ClaimType = sequelize.define('claim_type', {/* ... */})
const MaxClaimAmount = sequelize.define('max_claim_type', {/* ... */})
ClaimType.hasMany(MaxClaimAmount, {as: 'MaxClaims'})
This will add the attribute claim_typeId or claim_type_id to MaxClaimAmount (you will see the column appear in your table). Instances of ClaimType will get the accessors getMaxClaims and setMaxClaims (so you can set foreign id on table)
The next step would be creating your backend routes and using the accessor instance methods to set a foreign key. The accessor functions are called like so: (instance1).setMaxClaims(instance2)
Instances are returned from queries
Related
I am using sequelize for the first time and I having trouble getting my head around the association / foreign key relationships when pulling data into my jsons. I have some experience with SQL joins when I used PHP and I want to display the values that relate to the integer values in my diveSchool model.
Is there an easy way to do this without creating a complicated API?
The integers with ID relate to other tables and I have the foreign keys already created in pgadmin. Obviously the integers will mean nothing by themselves when displayed on the front-end.
module.exports = (sequelize, Sequelize) => {
const diveSchool = sequelize.define("diveSchools", {
diveSchoolID: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
allowNull: false
},
diveSchoolName: {
type: Sequelize.TEXT
},
diveSchoolLocation: {
type: Sequelize.TEXT
},
diveSchoolRegionID: {
type: Sequelize.INTEGER
},
diveCertificatesOfferedID: {
type: Sequelize.INTEGER
},
diveSpotsOfferedID: {
type: Sequelize.INTEGER
},
diveSchoolGeo: {
type: Sequelize.GEOMETRY('POINT'),
allowNull: false
},
},
{
timestamps: false
}, {});
diveSchool.associate = function(models){
diveSchool.belongsTo(models.diveRegion, {foreignKey: 'diveSchoolRegionID', as: 'diveRegion'})
diveSchool.belongsTo(models.diveCertification, {foreignKey: 'diveCertification', as: 'diveCertification'})
diveSchool.belongsTo(models.diveSpot, {foreignKey: 'diveSpotID', as: 'diveSpot'})
};
return diveSchool;
};
diveRegion.js
module.exports = (sequelize, Sequelize) => {
const diveRegion = sequelize.define("diveRegions", {
diveRegionID: {
type: Sequelize.INTEGER,
primaryKey: true
},
diveRegion: {
type: Sequelize.TEXT
}},
{
timestamps: false
},{});
diveRegion.associate = function(models){
diveRegion.hasMany(models.diveSchool, {as: 'diveRegion'})
};
return diveRegion;
};
diveCertifications.js
module.exports = (sequelize, Sequelize) => {
const diveCertification = sequelize.define("diveCertifications", {
diveCertificationID: {
type: Sequelize.INTEGER,
primaryKey: true
},
diveCertificationName: {
type: Sequelize.TEXT
}},
{
timestamps: false
},{});
diveCertification.associate = function(models){
diveCertification.hasMany(models.diveSchool, {as: 'diveCertificatesOfferedID'})
diveCertification.hasMany(models.diveCertsCompleted, {as: 'diveCertsCompletedID'})
};
return diveCertification;
};
diveSchool.controller.js API
exports.allDiveSchools = (req, res) => {
approvedDivingSchool.findAll({})
.then((approvedDivingSchool) => {
const diveSchoolsList = [];
for (i = 0; i < approvedDivingSchool.length; i++) {
diveSchoolsList.push(approvedDivingSchool[i].dataValues);
}
if (!approvedDivingSchool) {
return res.status(404).send({ message: "No dive schools stored in this region." });
}
res.status(200).send({
data: diveSchoolsList,
});
})
.catch((err) => {
res.status(500).send({ message: err.message });
});
};
Check out eager loading and how to code associations in the Sequelize docs. Note that I already had associations in the style above in model that didn't work however there are are examples of how to code include statements with the "example1.belongsTo(example2) style and then the include statements inside the 'where' methods in API's.
https://sequelize.org/master/manual/eager-loading.html
I have connected my app to an existing database. The database table currently has 3 entries but, when I query the model using findAll only an empty array is returned. Im not sure if this has something to do with the database already and existing and connecting to it through models. I am also syncing all files in the index file in the models directory.
//Courses Model for sequelize
const Sequelize = require('sequelize');
module.exports = (sequelize) => {
class Courses extends Sequelize.Model{}
Courses.init({
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
title: {
type: Sequelize.STRING,
allowNull: false
},
description: {
type: Sequelize.TEXT,
allowNull: false
},
estimatedTime: {
type: Sequelize.STRING,
},
materialsNeeded: {
type: Sequelize.STRING,
},
createdAt: {
type: Sequelize.DATE,
allowNull: false
},
updatedAt: {
type: Sequelize.DATE,
allowNull: false
},
userId: {
type: Sequelize.INTEGER,
references: { model: 'users', key: 'id' },
onDelete: 'CASCADE',
allowNull: false
}
}, {sequelize, modelName: 'courses'});
Courses.associate = (models) => {
models.courses.belongsTo(models.users, {foreignKey: "userId"});
};
return Courses
}
// Router with findAll query
const router = require('express').Router();
const db = require('../models/');
router.get('/', async(req, res) => {
try {
console.log(await db.courses.findAll());
} catch(err) {
console.error(err);
}
res.json({msg: "None"})
});
module.exports = router;
[This is the courses table currently][1]
[1]: https://i.stack.imgur.com/2KkK6.png
This is javascript level issuse I guess. You can't use await statement like that. Please try this out first.
onst router = require('express').Router();
const db = require('../models/');
router.get('/', async (req, res) => {
try {
const courese = await db.courses.findAll()
console.log(courses)
res.send({ courses })
} catch(err) {
console.error(err);
}
res.json({msg: "None"})
});
module.exports = router;
If the problem remains after running this, check if your config is pointing right database.
For checking whether this is problem.
Change your config to point existing database
Run the code
Change your config to point local database with different schema
Do the sequelize sync
Run the code
By doing so, you can check what is the real problem in your code.
Hello I am new to nodejs and currently I integrated Sequelize with NodeJs. I make different models and define associations but when I fetch data then I received following error
TypeError: include.model.getTableName is not a function
My Box Model
const Sequelize = require('sequelize');
const PostBox = require("./PostBox");
module.exports = function (sequelize) {
var Box = sequelize.define('box', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
references: {
model: PostBox,
key: "box_id" // this is the `posts.post_id` column - name it according to your posts table definition (the one defined in ./Post)
}
}
}, {timestamp: false});
Box.associate = function (models) {
Box.hasMany(models.PostBox, {targetKey: 'user_posts_id',foreginkey: 'id'});
};
return Box;
};
Post Box Model
const Sequelize = require('sequelize');
var Post = require('./Post');
module.exports = function (sequelize) {
var PostBox = sequelize.define('user_posts_boxes', {
id: {
type: Sequelize.INTEGER,
primaryKey: true
},
user_posts_id: {
type: Sequelize.INTEGER,
references: {
model: Post,
key: "id" // this is the `posts.post_id` column - name it according to your posts table definition (the one defined in ./Post)
}
},
box_id: {
type: Sequelize.INTEGER,
},
user_id: {
type: Sequelize.INTEGER
},
post_type_id: {
type: Sequelize.INTEGER
}
}, {timestamp: false}
);
PostBox.associate = function (models) {
PostBox.belongsTo(models.Post, {targetKey: 'id', foreignKey: 'user_posts_id'});
};
return PostBox;
};
This is my base model from where I am calling associated model
const Sequelize = require('sequelize');
var Box = require('./Box');
const sequelize = new Sequelize('store_db', 'root', 'root', {
host: 'localhost',
dialect: 'mysql'
});
const User = require("./User")(sequelize);
const PostBox = require("./PostBox")(sequelize);
const Post = require("./Post")(sequelize);
function findOneUer() {
return User.findOne({attributes: ['id', 'username']});
}
function findPostById(id) {
var post = PostBox.findOne({
attributes: ['id', "user_posts_id", "box_id"],
where: {id: id},
include: [
{model: Box, required: true}
]
});
return post;
}
module.exports = {findPostById: findPostById};
When I call findPostById(2) method from my base mode then I received this error.
I am trying to relate two tables.
The two tables are the employee table and the department table.
But for some reason the relationship is not made.
Here is my code and the error. What am I doing wrong?
I want to have multiple department tables in the employee table.
It works fine if I do not include it inside findAll. However, including them will cause problems.
//
module.exports = function (sequelize, DataTypes) {
const department = sequelize.define('DEPT', {
deptNo: {
field: 'DEPTNO',
type: DataTypes.INTEGER ,
primaryKey: true ,
autoIncrement: true
},
deptName: {
field: 'DEPTNAME',
type: DataTypes.STRING(32)
},
floor: {
field: 'FLOOR',
type: DataTypes.INTEGER
},
}, {
// don't use camelcase for automatically added attributes but underscore style
// so updatedAt will be updated_at
underscored: true,
// disable the modification of tablenames; By default, sequelize will automatically
// transform all passed model names (first parameter of define) into plural.
// if you don't want that, set the following
freezeTableName: true,
// define the table's name
tableName: 'DEPARTMENT'
});
department.associate = function (models){
department.belongsTo(models.EMP , {
foreignkey : "empId"
});
};
return department;
}
module.exports = function (sequelize, DataTypes) {
const employee = sequelize.define('EMP', {
empNo: {
field: 'EMPNO',
type: DataTypes.INTEGER ,
primaryKey: true ,
autoIncrement: true
},
empName: {
field: 'EMPNAME',
type: DataTypes.STRING(32)
},
title: {
field: 'TITLE',
type: DataTypes.STRING(32)
},
dNo: {
field: 'DNO',
type: DataTypes.INTEGER
},
salary: {
field: 'SALARY',
type: DataTypes.INTEGER
}
}, {
// don't use camelcase for automatically added attributes but underscore style
// so updatedAt will be updated_at
underscored: true,
// disable the modification of tablenames; By default, sequelize will automatically
// transform all passed model names (first parameter of define) into plural.
// if you don't want that, set the following
freezeTableName: true,
// define the table's name
tableName: 'EMPLOYEE'
});
employee.associations = function (models) {
employee.hasMany(models.DEPT , {
foreignkey : "empId"
});
};
return employee;
}
router.get('/', function(req, res, next) {
models.EMP.findAll({
include : [{model :models.DEPT}]
})
.then(results => {
res.json(results);
})
.catch( err => {
console.error(err);
});
});
ERROR : SequelizeEagerLoadingError: DEPT is not associated to EMP!
You should have smth like this:
const models = {};
glob.sync(path.join(__dirname, 'path to models e.g. **/*.js'))
.forEach(file => {
const model = sequelize.import(file);
models[model.name] = model;
});
Object.keys(models).forEach(model => {
if (models[model].associate) {
models[model].associate(models);
}
});
export {models};
I'm trying to understand how sequelize works on a simple example : User can have many posts and post can have only one user.
First question, I don't know if I have to use the migrations or the models with sync for creating my database. I mean, I have to put bearly the same code in both. This is the migration for the users table:
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Users', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
username: {
allowNull: false,
type: Sequelize.STRING,
unique: true
},
password: {
allowNull: false,
type: Sequelize.STRING
},
email: {
allowNull: false,
type: Sequelize.STRING,
unique: true
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Users');
}
};
And this is the Post model :
'use strict';
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define('User', {
username: DataTypes.STRING,
password: DataTypes.STRING,
email: DataTypes.STRING
}, {
classMethods: {
associate: (models) => {
User.hasMany(models.Post);
}
}
});
return User;
};
Do I also have to specify that the username, email can't be null and must be unique here in the model?
And how do I have to add the foreign key ? In one tutorial, they said me that the database add automaticly the foreign key but I don't think it works if I use the migrations, I have to set it manualy no?
For your version "sequelize": "^4.13.2":
classMethods and instanceMethods are removed.
Previous:
const Model = sequelize.define('Model', {
...
}, {
classMethods: {
associate: function (model) {...}
},
instanceMethods: {
someMethod: function () { ...}
}
});
New:
const Model = sequelize.define('Model', {
...
});
// Class Method
Model.associate = function (models) {
...associate the models
};
// Instance Method
Model.prototype.someMethod = function () {..}
See official docs Upgrade to V4
So for relations u should walkthrough this steps:
Import models
Call class "associate" method if exists
Export
Example:
// models/index.js
import fs from 'fs';
import path from 'path';
import Sequelize from 'sequelize';
import config from './config';
const sequelize = new Sequelize(config.db.url, config.db.options);
const DB = {};
// Import models
fs
.readdirSync(__dirname)
.filter(file => (file.indexOf('.') !== 0) && (file !== path.basename(__filename)) && (file.slice(-3) === '.js'))
.forEach((file) => {
const model = sequelize.import(path.join(__dirname, file));
DB[model.name] = model;
});
// Here u should call class method for associations
Object.keys(DB).forEach((modelName) => {
if ('associate' in DB[modelName]) {
DB[modelName].associate(DB);
}
});
DB.sequelize = sequelize;
DB.Sequelize = Sequelize;
export default DB;
All relations u can put in your models.
User:
// models/user.js
export default (sequelize, DataTypes) => {
const User = sequelize.define(
'users',
// Fields
{
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
},
// etc ...
},
// Options
{
timestamps: false, // <-- turn off timestamps
underscored: true, // <-- this option for naming with underscore. example: createdAt -> created_at
validate: {},
indexes: [],
},
);
User.associate = (models) => {
User.hasMany(models.post, {
// ...
});
User.hasMany(models.comment, {
// ...
});
// OR
models.user.hasMany(models.post, {
// ...
});
};
return User;
};
Post:
// models/post.js
export default (sequelize, DataTypes) => {
const Post = sequelize.define(
'posts',
// Fields
{
// ...
},
// Options
{
// ...
},
);
Post.associate = (models) => {
Post.belongsTo(models.user, {
// ...
});
// OR
models.post.belongsTo(models.user, {
// ...
});
};
return Post;
};
Do I also have to specify that the username, email can't be null and
must be unique here in the model?
Yes u should define all things in your models, such as keys, relations, whatever. Because your app use models for actions with database.
And how do I have to add the foreign key ? In one tutorial, they said
me that the database add automaticly the foreign key but I don't think
it works if I use the migrations, I have to set it manualy no?
Actually u cant define composite keys in migrations that creates the table and fields.
Best practise for migrations should be like this:
000000_create_users_table
000001_add_foreign_keys_to_users_table
000002_add_new_field_to_users_table
etc...
So u should add all things manually in migrations.
For adding indexes in migrations you should use queryInterface.addIndex
module.exports = {
up: queryInterface => queryInterface.addIndex(
'users',
{
unique: true,
fields: ['username', 'email'],
// if u want to rename u can use:
// name: 'whatever'
// by convention default name will be: table_field1_fieldN
},
),
down: queryInterface => queryInterface.removeIndex(
'users',
'users_username_email', // <-- this name by convention, but u can rename it
),
};
For "keys" you should use queryInterface.addConstraint
Primary Key
queryInterface.addConstraint('Users', ['username'], {
type: 'primary key',
name: 'custom_primary_constraint_name'
});
Foreign Key
queryInterface.addConstraint('Posts', ['username'], {
type: 'FOREIGN KEY',
name: 'custom_fkey_constraint_name',
references: { //Required field
table: 'target_table_name',
field: 'target_column_name'
},
onDelete: 'cascade',
onUpdate: 'cascade'
});
Check all API References
You are right you have to manually set the foreign key relations.
Here is official documentation link : http://docs.sequelizejs.com/manual/tutorial/associations.html
You can try following code:
var user_object = require('your_file_path');
var post_object = require('your_file_path');
user_object.hasMany(post_object, {
foreignKey: 'user_id',
sourceKey: 'user_id',
onDelete: 'cascade',
as:'Posts',
});
post_object.belongsTo(user_object, {
foreignKey: 'user_id',
sourceKey: 'user_id',
onDelete: 'cascade',
as:'Posts',
});
I am really just restrucuring your code.
// Create One database config file
var Sequelize=require('sequelize');
var connection=new Sequelize('project','user','password',{
dialect:'mysql',
logging:false
});
connection.authenticate()
.then(() => {
console.log("Connected to database");
})
.catch(err => {
//console.error("Can't connect to database :(\n", err);
});
module.exports={
database:connection,
}
//Your User Schema File
var database = require('your_file_path/DatabaseConnection').database;
var Sequelize = require('sequelize');
var Users = database.define('users', {
username: {
allowNull: false,
type: Sequelize.STRING,
unique: true
},
password: {
allowNull: false,
type: Sequelize.STRING
},
email: {
allowNull: false,
type: Sequelize.STRING,
unique: true
}
}, {
underscored: true
},hooks: {
beforeCreate: (user, option) => {
users.password = encrypto.encryptEntity(user.password);
//for automatic encryption of password
},
}
);
Users.sync();
//id, updated_at , and modified_at will be maintained by default
module.exports = {
Users
}
// your post file path
var Posts = database.define('posts', {
post_content: {
allowNull: false,
type: Sequelize.STRING,
unique: true
}
}, {
underscored: true
});
//importing User
var Users = require('file_path')
Users.hasMany(Posts, {
foreignKey: 'user_id',
sourceKey: 'user_id',
onDelete: 'cascade',
as:'Posts',
});
Posts.belongsTo(Users, {
foreignKey: 'user_id',
sourceKey: 'user_id',
onDelete: 'cascade',
as:'Users',
});
// two way binding.
Posts.sync();
BY maintaining Relation you can easily update data using setter and getter methods
Posts.setUsers(user_object);
// above code will automatically put the user_id found in user_object
//for select query you can use:
Users.findOne({
where:{
id:user_id
},
include: [{
model: Posts,
attributes: ['post_content'],
as: "Posts"
}//this will bring every posts user has created
})
I think above coding standard will make your code looks cleaner and will be more helpful for larger projects.
Hope this helps.