Node Sequelize migrations/models is it possible to share the same code? - node.js

I'm new at Sequelize so be patient.
I started up a new project using Sequelize
and migrations so I've got like this:
migrations/20150210104840-create-my-user.js:
"use strict";
module.exports = {
up: function(migration, DataTypes, done) {
migration.createTable("MyUsers", {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: DataTypes.INTEGER
},
first_name: {
type: DataTypes.STRING
},
last_name: {
type: DataTypes.STRING
},
bio: {
type: DataTypes.TEXT
},
createdAt: {
allowNull: false,
type: DataTypes.DATE
},
updatedAt: {
allowNull: false,
type: DataTypes.DATE
}
}).done(done);
},
down: function(migration, DataTypes, done) {
migration.dropTable("MyUsers").done(done);
}
};
models/myuser.js:
"use strict";
module.exports = function(sequelize, DataTypes) {
var MyUser = sequelize.define("MyUser", {
first_name: DataTypes.STRING,
last_name: DataTypes.STRING,
bio: DataTypes.TEXT
}, {
classMethods: {
associate: function(models) {
// associations can be defined here
}
}
});
return MyUser;
};
as you can see the table definition
is both on the migration and the model file.
I'm wondering if there is a way to share
the code ?
I mean I don't like to have logic in two files
if a field change I've to update twice.
UPDATE
following the Yan Foto example below
a different way may be cleaner.
schemas/users
'use strict';
module.exports = {
name: 'users',
definition : function(DataTypes) {
return {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true
},
firstname: {
type:DataTypes.STRING
},
lastname: {
type:DataTypes.STRING
},
email: {
type: DataTypes.STRING,
unique: true
},
username: {
type:DataTypes.STRING,
unique: true
}
};
}
};
models/users
'use strict';
var Schema = require('../schemas/users');
module.exports = function(sequelize, DataTypes) {
return sequelize.define(
Schema.name,
Schema.definition(DataTypes),
{
freezeTableName: true ,
instanceMethods: {
countTasks: function() {
// how to implement this method ?
}
}
}
);
};
migrations/20150720184716-users.js
'use strict';
var Schema = require('../schemas/users');
module.exports = {
up: function (queryInterface, Sequelize) {
return queryInterface.createTable(
Schema.name,
Schema.definition(Sequelize)
);
},
down: function (queryInterface, Sequelize) {
return queryInterface.dropTable(Schema.name);
}
};

I wondered the same thing as I started using sequelize and here is my solution. I define my models as bellow:
module.exports = {
def: function(DataTypes) {
return {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true
},
username: DataTypes.STRING,
password: DataTypes.STRING,
createdAt: DataTypes.DATE,
updatedAt: DataTypes.DATE,
}
},
config: {}
};
Where def defines the attributes and config is the optional options object accepted by define or migration methods. And I import them using the following code:
fs.readdirSync(__dirname + '/PATH/TO/models')
.filter(function(file) {
return (file.indexOf('.') !== 0) && (file !== basename);
})
.forEach(function(file) {
var name = file.substring(0, file.lastIndexOf(".")),
definition = require(path.join(__dirname + '/models', file));
sequelize['import'](name, function(sequelize, DataTypes) {
return sequelize.define(
name,
definition.def(DataTypes),
definition.config
);
});
});
For the migrations I have a similar approach:
const path = require('path');
module.exports = {
up: function (queryInterface, Sequelize) {
return queryInterface.createTable(
'users',
require(path.join(__dirname + '/PATH/TO/models', 'user.js')).def(Sequelize)
);
},
down: function (queryInterface, Sequelize) {
return queryInterface.dropTable('users');
}
};

Related

Sequelize Association Error: A is not associated to B

I'm using Sequelize for my Postgres database. I have a Messages and a Users table; a user has many messages and a message belongs to a user. I've defined my models as follows:
User
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define('User', {
firstName: {
type: DataTypes.STRING,
allowNull: false
},
lastName: {
type: DataTypes.STRING,
allowNull: false
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
validate: {
isEmail: true,
}
},
password: {
type: DataTypes.STRING,
allowNull: false,
validate: {
len: [8, 50],
}
},
}, {
modelName: 'User',
});
User.associate = (models) => {
User.hasMany(models.Message, { foreignKey: 'userId', as: 'Messages' })
}
return User;
};
Message
module.exports = (sequelize, DataTypes) => {
const Message = sequelize.define('Message', {
content: {
allowNull: false,
type: DataTypes.STRING,
validate: {
len: [1, 248],
}
},
userId: {
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: 'Users',
key: 'id'
}
},
likes: {
defaultValue: 0,
type: DataTypes.INTEGER
},
}, {
modelName: 'Message',
});
Message.associate = (models) => {
Message.belongsTo(models.User, { foreignKey: 'userId', as: 'User', onDelete: 'CASCADE' })
}
return Message;
};
And here's how I'm testing them:
User.create({
firstName: 'Test', lastName: 'Test', email: 'test#test.com', password: '87654321'
}).then((newUser) => {
console.log(newUser.get())
})
Message.bulkCreate([
{ content: "Hello", likes: 0, userId: 1 },
{ content: "Hello World", likes: 0, userId: 1 },
{ content: "Hello World 2", likes: 123, userId: 1 }
])
.then((newMessages) => {
console.log(newMessages)
})
const findAllWithMessages = async () => {
const users = await User.findAll({
include: [{
model: Message
}]
});
console.log(JSON.stringify(users, null));
}
Here's my Migration file to create the users table:
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable('Users', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
firstName: {
type: Sequelize.STRING,
allowNull: false
},
lastName: {
type: Sequelize.STRING,
allowNull: false
},
email: {
type: Sequelize.STRING,
allowNull: false,
unique: true,
validate: {
isEmail: true,
}
},
password: {
type: Sequelize.STRING,
allowNull: false,
validate: {
len: [8, 50],
}
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: async (queryInterface, Sequelize) => {
await queryInterface.dropTable('Users');
}
};
And the messages table:
'use strict';
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable('Messages', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
userId: {
type: Sequelize.INTEGER,
allowNull: false,
references: {
model: 'Users',
key: 'id',
as: 'userId',
},
onUpdate: 'CASCADE',
onDelete: 'SET NULL',
},
content: {
allowNull: false,
type: Sequelize.STRING,
validate: {
len: [1, 248],
}
},
likes: {
defaultValue: 0,
type: Sequelize.INTEGER
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: async (queryInterface, Sequelize) => {
await queryInterface.dropTable('Messages');
}
};
I'm registering my models and associations using Sequelize CLI out of the box code when you run sequelize-cli init:
const fs = require('fs');
const path = require('path');
const Sequelize = require('sequelize');
const basename = path.basename(__filename);
const env = process.env.NODE_ENV || 'development';
const config = require(__dirname + '/../config/config.json')[env];
const db = {};
let sequelize;
if (config.use_env_variable) {
sequelize = new Sequelize(process.env[config.use_env_variable], config);
} else {
sequelize = new Sequelize(config.database, config.username, config.password, config);
}
fs
.readdirSync(__dirname)
.filter(file => {
return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js');
})
.forEach(file => {
const model = require(path.join(__dirname, file))(sequelize, Sequelize.DataTypes);
db[model.name] = model;
});
Object.keys(db).forEach(modelName => {
if (db[modelName].associate) {
db[modelName].associate(db);
}
});
const User = require('./user')(sequelize, Sequelize.DataTypes)
const Message = require('./message')(sequelize, Sequelize.DataTypes)
db.models = { User, Message }
db.sequelize = sequelize;
db.Sequelize = Sequelize;
db.DataTypes = Sequelize.DataTypes
module.exports = db;
Finally, when I run findAllWithMessages(), I'm getting this error UnhandledPromiseRejectionWarning: SequelizeEagerLoadingError: Message is not associated to User!
I can confirm that the models are being created and that the association between the models work because when I run a raw SQL query select * from "Messages" as a inner join "Users" as b on b.id = a."userId" where a."userId"=1; I get the correct results. So I'm assuming its a Sequelize thing.
Any help is appreciated!
I've found my issue. In the code I was importing from db.models = { User, Message } so this block Object.keys(db).forEach(modelName)... wasn't associating the models I was using. Essentially, I was calling the .associate function on instances of the models that were different than the instances I was using.

Sequelize especial methods don't get created when Using sequelize migrations

I am struggling when using sequelize migrations and a many-to-many relationship between Users and Roles.
This is the Users model:
'use strict';
module.exports = (sequelize, DataTypes) => {
const user = sequelize.define('Users', {
username: DataTypes.STRING,
name: DataTypes.STRING,
email: DataTypes.STRING,
password: DataTypes.STRING
}, {});
user.associate = function(models) {
// associations can be defined here
user.belongsToMany(models.Roles, {
through: models.UserRoles
});
};
return user;
};
This is the Roles model:
'use strict';
module.exports = (sequelize, DataTypes) => {
const role = sequelize.define('Roles', {
name: DataTypes.STRING
}, {});
role.associate = function(models) {
// associations can be defined here
role.belongsToMany(models.Users, {
through: models.UserRoles
});
};
return role;
};
This is the "create-user" migration:
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Users', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
username: {
type: Sequelize.STRING,
allowNull: true,
len: [0, 20]
},
name: {
type: Sequelize.STRING,
allowNull: true,
},
email: {
type: Sequelize.STRING,
allowNull: false
},
password: {
type: Sequelize.STRING,
allowNull: false
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Users');
}
};
This is the "create-role" migration:
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Roles', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
name: {
type: Sequelize.STRING
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Roles');
}
};
This is the userRoles Model:
'use strict';
module.exports = (sequelize, DataTypes) => {
const user_role = sequelize.define('UserRoles', {
userId: DataTypes.INTEGER,
roleId: DataTypes.INTEGER
}, {});
user_role.associate = function(models) {
// associations can be defined here
};
return user_role;
};
And last one the "user-roles" migration:
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('UserRoles', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
userId: {
primaryKey: true,
type: Sequelize.INTEGER,
allowNull: false,
references: {
model: 'Users',
key: 'id'
}
},
roleId: {
primaryKey: true,
type: Sequelize.INTEGER,
allowNull: false,
references: {
model: 'Roles',
key: 'id'
}
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('UserRoles');
}
};
The problem happens when I try to access to the user.setRoles() from a controller:
exports.signup = (req, res) => {
console.log('creating new user', req.body.username);
// Save User to Database
User.create({
username: req.body.username,
email: req.body.email,
password: bcrypt.hashSync(req.body.password, 8)
})
.then(user => {
console.log('USER ADDED');
if (req.body.roles) {
Role.findAll({
where: {
name: {
[Op.or]: req.body.roles
}
}
}).then(roles => {
console.log('ROLES ', roles);
user.setRoles(roles).then(() => {
res.send({ message: "User was registered successfully!" });
});
});
} else {
console.log('NO ROLES > Normal User');
// user role = 1
user.setRoles([1]).then(() => {
res.send({ message: "User was registered successfully!" });
});
}
})
.catch(err => {
console.log('ERROR: ', err);
res.status(500).send({ message: err.message });
});
};
When I console.log the user using : console.log(Object.keys(user.__proto__)) I get this array where the special methods haven't been created, any idea what I am doing wrong?
Array(7) ["_customGetters", "_customSetters", "validators", "_hasCustomGetters", "_hasCustomSetters", "rawAttributes", "_isAttribute"]
Many thanks for your help!
Just call all associate functions after registering models. For instance:
const models = path.join(__dirname, 'models')
const db = {}
fs.readdirSync(models)
.filter(function (file) {
return (file.indexOf('.') !== 0) && (file.slice(-3) === '.js')
})
.forEach(function (file) {
var model = sequelize['import'](path.join(models, file))
db[model.name] = model
})
Object.keys(db).forEach(function (modelName) {
if (db[modelName].associate) {
db[modelName].associate(db)
}
})

sequelize-cli db:migrate doesn't generate association table

I have a User model (with associated migration file created by sequelize-cli):
'use strict';
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define('User', {
dispName: DataTypes.STRING,
email: DataTypes.STRING,
phoneNum1: DataTypes.STRING
}, {});
User.associate = function (models) { // associations added manually
User.belongsToMany(models.Role, { through: 'UserRoles', foreignKey: 'userId' });
};
return User;
};
here's the generated migration file:
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Users', {
cognitoId: { // modified: was an auto-incrementing integer
allowNull: false,
primaryKey: true,
type: Sequelize.STRING(100)
},
dispName: {
type: Sequelize.STRING(100) // modified: was just plain STRING
},
email: {
type: Sequelize.STRING(100) // modified: was just plain STRING
},
phoneNum1: {
type: Sequelize.STRING(15) // modified: was just plain STRING
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Users');
}
};
and a Role model (with associated migration file created by sequelize-cli):
'use strict';
module.exports = (sequelize, DataTypes) => {
const Role = sequelize.define('Role', {
name: DataTypes.STRING
}, {});
Role.associate = function (models) { // associations added manually
Role.belongsToMany(models.User, { through: 'UserRoles', foreignKey: 'roleId' });
};
return Role;
};
here's the generated migration file for Role:
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Roles', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
name: {
type: Sequelize.STRING(100), // modified: was just plain STRING
unique: true // Added manually
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Roles');
}
};
When I run sequelize-cli db:migrate, the table UserRoles is not created. Why is that?
The Associations page of the Sequelize manual seems to suggest that defining the associations in the model file is all that is required...
Research:
I seem to have done what is suggested in this answer to the question Sequelize not creating model association columns but doesn't seem to be working for me (the answer isn't accepted either).
Not quite what I need: Querying association tables in Sequelize
Nor this: Sequelize how to use association table?

NodeJs + Sequelize + Express extra association key addedwhen query-ing

I have 2 tables: Countries and Spots. A country can have many spots and a spot belongs to one country.
I have generated the migrations necessary with sequelize for the 2 tables:
Countries.js
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Countries', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
name: {
allowNull: false,
unique: true,
type: Sequelize.STRING
},
createdAt: {
allowNull: false,
defaultValue: Sequelize.literal('NOW()'),
type: Sequelize.DATE,
},
updatedAt: {
allowNull: false,
defaultValue: Sequelize.literal('NOW()'),
type: Sequelize.DATE
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Countries');
}
};
Spots.js
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Spots', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
name: {
allowNull: false,
type: Sequelize.STRING
},
wind: {
type: Sequelize.FLOAT
},
country_id: {
type: Sequelize.INTEGER,
references: {
model: 'Countries', // name of Target table
key: 'id', // key in Target table that we're referencing
},
onDelete: 'CASCADE',
},
createdAt: {
allowNull: false,
defaultValue: Sequelize.literal('NOW()'),
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
defaultValue: Sequelize.literal('NOW()'),
type: Sequelize.DATE
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Spots');
}
};
Everything works ok. I define some routes and some controllers and I try to do Spots.findAll() in my controller
const models = require('../models/index')
const Spot = models.Spot
exports.index = async (req, res, next) => {
const spots = await Spot.findAll()
res.status(200).json(spots)
}
However the query Spot.findAll() tries to ask for CountryId which is a key that obviously doesn't exist and I do not wish for it to exist.
Executing (default): SELECT `id`, `name`, `wind`, `country_id`, `createdAt`, `updatedAt`, `CountryId` FROM `Spots` AS `Spot`;
(node:13027) UnhandledPromiseRejectionWarning: SequelizeDatabaseError: Unknown column 'CountryId' in 'field list'
These are the spot and countries models:
Country.js
'use strict';
module.exports = (sequelize, DataTypes) => {
const Country = sequelize.define('Country', {
name: DataTypes.STRING
}, {});
Country.associate = function(models) {
// associations can be defined here
Country.hasMany(models.Spot)
};
return Country;
};
Spot.js
'use strict';
module.exports = (sequelize, DataTypes) => {
const Spot = sequelize.define('Spot', {
name: DataTypes.STRING,
wind: DataTypes.FLOAT,
country_id: DataTypes.INTEGER
}, {});
Spot.associate = function(models) {
// associations can be defined here
Spot.belongsTo(models.Country, {
foreignKey: 'country_id'
});
Spot.hasMany(models.Favorite)
};
return Spot;
};
I added the foreign_key attribute to belongs_to as I thought that the error surely comes from the associations(I still think it does).
Why does it happen and how to fix it?
The problem is because you are mixing everything here please follow one convention either camelCase or snack_case.
Write country_id as countryId and change your table names to lowercase and you will good to go.

Create Association in Sequelize

I am using "sequelize": "^5.8.6" and have created my project structure using "sequelize-cli": "^5.4.0". I would like to create associations so that:
One company has many ratings
I have created a company model, which looks like that:
'use strict';
module.exports = (sequelize, DataTypes) => {
const Company = sequelize.define('Company', {
name: DataTypes.STRING,
symbol: DataTypes.STRING,
}, {});
Company.associate = function(models) {
Company.hasMany(models.Rating);
};
return Company;
};
My Rating model looks like that:
'use strict';
module.exports = (sequelize, DataTypes) => {
const Rating = sequelize.define('Rating', {
action: DataTypes.STRING,
}, {});
Rating.associate = function(models) {
Rating.belongsTo(models.Company);
// associations can be defined here
};
return Rating;
};
My Company Migration look like the following:
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Companies', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
name: {
type: Sequelize.STRING
},
symbol: {
type: Sequelize.STRING
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Companies');
}
};
My Rating migration looks like the following:
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Ratings', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
companyid: {
type: Sequelize.INTEGER,
references: {
model: 'Company',
key: 'id',
},
onUpdate: 'CASCADE',
onDelete: 'SET NULL',
},
action: {
type: Sequelize.STRING
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Ratings');
}
};
When running, I get the following error:
> npx sequelize-cli db:migrate
ERROR: Can't create table `test_db`.`ratings` (errno: 150 "Foreign key constraint is incorrectly formed")
Any suggestions what I am doing wrong?
I appreciate your replies!
If you haven't just left it out of your code, your company model association should read:
Company.associate = function(models) {
Company.hasMany(models.Rating, {
foreignKey: 'companyid',
targetKey: 'id'
});
};
And your rating model should read:
Rating.associate = function(models) {
Rating.belongsTo(models.Company, {
// associations can be defined here
foreignKey: 'companyid',
targetKey: 'id'
});
};

Resources