How sequelize works? - node.js

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.

Related

Node Sequelize Relational Challenge

I have three tables (all associated model classnames use PascalCase)
schools school_codes course
------ ------ ------
id (pk) code (pk) name
name school_id (fk) school_code (fk)
I'm trying to define sequelize relations, so that this Course lookup returns the associated School:
const courseWithSchool = await models.Course.findOne({
include: [{
model: models.School,
required: true,
}],
})
The mysql for this is very simple.
mysql> select c.*, s.* from courses c inner join school_codes sc on c.school_code = sc.code inner join schools s on s.id = sc.school_id;
How do I define the relations in sequelize models (without modifying existing schema)? Thanks!
Here are the model definitions I have:
schools.js
module.exports = (sequelize, DataTypes) => {
const School = sequelize.define('School', {
name: DataTypes.STRING,
}, { underscored: true, freezeTableName: true, tableName: 'schools' })
return School
}
course.js
module.exports = (sequelize, DataTypes) => {
const Course = sequelize.define('Course', {
id: {
type: DataTypes.STRING,
primaryKey: true,
},
name: DataTypes.STRING,
school_code: {
type: DataTypes.STRING,
references: {
model: 'school_codes',
key: 'code',
}
}
}, { underscored: true, freezeTableName: true, tableName: 'courses' })
return Course
}
schoolcode.js
module.exports = (sequelize, DataTypes) => {
const SchoolCode = sequelize.define('SchoolCode', {
code:{
type: DataTypes.STRING,
primaryKey: true,
references: {
model: 'courses',
key: 'school_code'
}
},
school_id: {
type: DataTypes.INTEGER,
references: {
model: 'schools',
key: 'id',
},
},
}, { underscored: true, freezeTableName: true, tableName: 'school_codes', })
return SchoolCode
}
I'm just looking for the relations to add to the bottom of each model definition - example...
// School.associate = function (models) {
// School.belongsToMany(models.Course, {
// through: 'school_codes',
// foreignKey: 'school_id',
// otherKey: 'code'
// })
// }
We can keep association in its respective model. I prefer to keep association in respective master table rather than mapping table. The idea is to associate source model to target model and its relationship in both direction. For example let us say source model School has one SchoolCode target model and its reverse relation
//school.model.js
module.exports = (sequelize, DataTypes) => {
const School = sequelize.define('school', {
name: DataTypes.STRING,
}, { underscored: true, freezeTableName: true, tableName: 'schools' })
School.associate = function ({SchoolCode, Course}) {
School.hasOne(SchoolCode, {
foreignKey: 'school_id',
})
SchoolCode.belongsTo(School, {foreignKey: 'school_id'})
School.belongsToMany(Course, { through: SchoolCode , foreignKey : 'school_id'}); //added new
}
return School;
}
//course.model.js
module.exports = (sequelize, DataTypes) => {
const Course = sequelize.define('course', {
id: {
type: DataTypes.STRING,
primaryKey: true,
},
name: DataTypes.STRING,
school_code: {
type: DataTypes.STRING,
references: {
model: 'school_codes',
key: 'code',
}
}
}, { underscored: true, freezeTableName: true, tableName: 'courses' })
Course.associate = function ({SchoolCode, School}) {
Course.hasMany(SchoolCode, {
foreignKey: 'code',
})
Course.belongsToMany(School, { through: SchoolCode, foreignKey : 'code'}); //added new
}
return Course;
}
Finally the third model of SchoolCode (Mapping table).
Note that we don't have to add a reference school_code. It is a primaryKey code of same table. We use references mainly to define the foreign keys, no need for reverse definition here.
Hence commented that part from code below.
module.exports = (sequelize, DataTypes) => {
const SchoolCode = sequelize.define('SchoolCode', {
code:{
type: DataTypes.STRING,
primaryKey: true,
// references: {
// model: 'courses',
// key: 'school_code'
// }
},
school_id: {
type: DataTypes.INTEGER,
references: {
model: 'school',
key: 'id',
},
},
}, { underscored: true, freezeTableName: true, tableName: 'school_codes', })
return SchoolCode
}
References : https://sequelize.org/master/manual/assocs.html
You can define relations like
SchoolCode.belongsTo(School, { foreignKey: 'school_id', targetKey: 'id' });
Course.belongsTo(SchoolCode, { foreignKey: 'school_code', targetKey: 'code' });

How to define foreign key constraint in postgres using sequelize ORM?

I am trying to create one to many foreign key constraint between two tables. A client can have many environments.
Here is my snippet in the model.
Client.associate = function(models) {
Client.hasMany(models.Enviornment, {as: 'enviornments', foreignKey: 'clientId'})
};
The solution is here.
The client has many environments. (one to many association )
Here is a code snippet for a model client.
module.exports = (sequelize, DataTypes) => {
let client = sequelize.define ('client', {
id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true, field: 'id' },
...
}, {
associate: models => {
client.hasMany (models.enviornment, {
foreignKey: { name: 'client_id', allowNull: false }
});
},
});
return client;
};
Here is a code snippet for a model enviornment.
module.exports = (sequelize, DataTypes) => {
let enviornment = sequelize.define ('enviornment', {
id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true, field: 'id' },
...
}, {
associate: models => {
enviornment.belongsTo (models.client, {
foreignKey: { name: 'client_id', allowNull: false }
});
},
});
return enviornment;
};

Add default role to many-many relation in Sequelize.js

I have two Sequelize.js models what are connected by many-to-many relation.
User:
module.exports = (sequelize, DataTypes) => {
const model = sequelize.define('User', {
email: {
type: DataTypes.STRING,
unique: true,
allowNull: false
},
password: {
type: DataTypes.CHAR,
length: 60,
allowNull: false
}
}
});
model.associate = models => {
model.belongsToMany(models.Role, {
hooks: true,
through: 'user_roles'
})
};
return model;
};
Role:
module.exports = (sequelize, DataTypes) => {
const model = sequelize.define('Role',
{
name: {
type: DataTypes.STRING,
unique: false,
allowNull: false
}
}
);
model.associate = models => {
model.belongsToMany(models.User, {
hooks: true,
through: 'user_roles'
});
};
return model;
};
Table user_roles is created automatically.
How to set default role what will be inserted into user_roles while creating new User? Is there way to define it in models definition or I am just supposed to create User new user and then create relation in one transaction?
First you need to create a model for the user_roles after creating that you have to set a hook inside your user model that will automatically insert roles in to your user_roles table.
This is how you can do it.
For Example :
User Model
const { hooks } = require('./user-role.hook');
module.exports = (sequelize, DataTypes) => {
const model = sequelize.define('User', {
email: {
type: DataTypes.STRING,
unique: true,
allowNull: false
},
password: {
type: DataTypes.CHAR,
length: 60,
allowNull: false
}
}, { hooks });
model.associate = models => {
model.belongsToMany(models.Role, {
hooks: true,
through: 'user_roles'
})
};
return model;
};
Hook file
exports.hooks = {
afterCreate: (User, payload) => {
AddUserRole(User, payload);
}
}
function AddUserRole(User, payload) {
let InsertArr = {
user_id: User._id,
role_id: 1 // pass default role id
}
model.UserRole.create(InsertArr);
}

Sequelize associate not created

A very straightforward question. Am using nodejs 5.6, express 4 and sequelize 4. In the program, I have a user object which belongs to company object.
var User = sequelize.define('user', {
username: {
type: Sequelize.STRING
},
password: {
type: Sequelize.STRING
}
});
const Company= sequelize.define('companny', {
record: {
type: Sequelize.JSON
}
});
Company.associate = function(models) {
console.log(models)
Company.hasMany(models.User, {
foreignKey: {
// name: 'user',
allowNull: true
}
})
}
when running the program, the database is created with user and company table, but there is no foreign key in eight user or company. And the console outputs nothing. Any suggestion?
Finally, the index.js file here answers my question.
I would normally use this method to create my models:
// User model
export default (sequelize, DataTypes) => {
const User = sequelize.define('User', {
email: {
allowNull: false,
type: DataTypes.STRING,
unique: true
},
password: {
allowNull: false,
type: DataTypes.STRING,
}
});
return User;
};
// Company model
export default (sequelize, DataTypes) => {
const Company = sequelize.define('Company', {
record: {
type: DataTypes.JSON
}
});
Company.associate = (models) => {
console.log(models)
Company.hasMany(models.User, {
foreignKey: 'userId'
})
}
return Company;
};

Update with association using Sequelize.js

I am trying to update with association using sequelize.js.
I have tried give example on stackoverflow namely the following links:
Sequelize update with association
Sequelize update with association
Updating attributes in associated models using Sequelize
all of these links did not get me to the goal i am trying to accomplish.
My model is as follow, I have a country module and a city module. a country has many cities. please refer to the module bellow.
Please advise.
country.js file
module.exports = function (sequelize, DataTypes) {
var country= sequelize.define('COUNTRY', {
COUNTRY_ID: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
COUNTRY_NAME: DataTypes.STRING,
COUNTRY_CURRENCY: DataTypes.STRING
}, {
freezeTableName: true,
classMethods: {
associate: function (models) {
COUNTRY_ID.hasMany(models.CITIES, {
foreignKey: 'COUNTRY_ID'
})
}
},
instanceMethods: {
updateAssociation: function (onSuccess, onError) {
country.findAll({
where: {
COUNTRY_ID: req.params.country_id
},
include: [
{
model: sequelize.import('./cities.js'),
}
]
})
})
.then(country =>{
const updatePromises = country.map(countries =>{
return countries.updateAttributes(req.body);
});
const updatePromisescities = list.CITY.map(cities =>{
return cities.updateAttributes(req.body.CITYs[0]);
});
return sequelize.Promise.all([updatePromises, updatePromisescities ])
}).then(onSuccess).error(onError);
}
}
});
return country;
};
city.js file
module.exports = function (sequelize, DataTypes) {
var CITY = sequelize.define('LIST_CODE', {
CITY_ID: {
type: DataTypes.INTEGER,
primaryKey: true
},
COUNTRY_ID: {
type: DataTypes.INTEGER,
primaryKey: true
}
}, {
freezeTableName: true,
timestamps: false,
classMethods: {
associate: function (models) {
// associations can be defined here
CITY.belongsTo(models.COUNTRY, {
foreignKey: 'COUNTRY_ID'
})
}
}
});
return CITY;
};

Resources