Sequelize migrations - node.js

I am new to Sequelize and would like to know if anyone knows if there is a way to avoid the code duplication in Sequelize when using the migration functionality?
What I mean is that in the migration you would have something like this:
//migration-xyz.js
module.exports = {
up: function(migration, DataTypes, done) {
migration.createTable('Users',
{
name: DataTypes.STRING,
surname: DataTypes.STRING
});
},
down: function(migration, DataTypes, done) {
// logic for reverting the changes
}
}
But then the model would look something like this:
//user.js
module.exports = function(sequelize, DataTypes) {
var User = sequelize.define('User', {
name: DataTypes.STRING,
surname: DataTypes.STRING
});
return User;
};
Is there a way to get rid of the code duplication?
Thanks for the help! :)

As #WiredPrairie referenced in his/her comment, migrations and the attributes on a model are distinct things. Often we will create a model that has certain attributes and the associated migration to create the corresponding table. Later on more attributes will be added to the model and we will need to create a migration that adds columns for just the new attributes.
A way to remove the duplication would be to use sequelize.sync which creates your database from the model files. This has downsides though and should not be used on complex applications that are being deployed in different environments.
Maybe in the future the sequelize-cli will have a create:migration --model [modelfile] option where it will create the table for the corresponding model. This would have the same "duplication" of code but would make things a little faster.

When you initially ran the migrations via sequelize-cli
it set up the folder structure as:
-config
-config/config.json
-migrations
-models
-models/index.js
If you make sure your model file:
//user.js
module.exports = function(sequelize, DataTypes) {
var User = sequelize.define('User', {
name: DataTypes.STRING,
surname: DataTypes.STRING
});
return User;
};
is in the models directory, it will automatically be picked up when you
require('./models');
So, I've used the following code to import the new models for that revision of the migration:
//migrations-xyz.js
var Models = require('../models');
module.exports = {
up: function (queryInterface, Sequelize) {
/*
Add altering commands here.
Return a promise to correctly handle asynchronicity.
Example:
return queryInterface.createTable('users', { id: Sequelize.INTEGER});
*/
var tables = _.map(Models.sequelize.models, function(def){
var model = Models[def],
options = model.options,
tableName = model.tableName,
attributes = model.tableAttributes;
return queryInterface.createTable(tableName, attributes, options);
});
return Promise.all(tables);
},
down: function (queryInterface, Sequelize) {
/*
Add reverting commands here.
Return a promise to correctly handle asynchronicity.
Example:
return queryInterface.dropTable('users');
*/
return Models.sequelize.drop({logging: true});
}
};

Related

Why when I generate a model with sequelize has a different structure than others that includes a class instead of a const?

I'm new with sequelize and I've seen many tutorials on how to create models with sequelize, but when I did, this is the structure that created it.
'use strict';
const {
Model
} = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class User extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
// define association here
}
};
Users.init({
nickname: DataTypes.STRING,
password: DataTypes.STRING
}, {
sequelize,
modelName: 'Users',
});
return User;
};
But the other structure that I see is "most popular" is this.
'use strict';
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define('User', {
nickname: DataTypes.STRING,
password: DataTypes.STRING
},{
underscored: true
});
return User;
};
How can I generate a model with the second structure using "sequelize model:generate" in console?
Thanks for your time.
According to the official documentation both methods to define a model are equal to each other. define is more popular because it's an older approach then classes and that's all. I recommend to use a newer one because I suppose it will have a longer life cycle and support.

Sequelize doesn't read the new modelname after I do refactor (remove s in end modelname)

I've a backend app built in Express with Sequelize ORM.
Here is my code;
user.js (model)
'use strict';
module.exports = (sequelize, DataTypes) => {
const user = sequelize.define('user', {
username: DataTypes.STRING,
}, {
// tableName: 'user'
});
user.associate = function(models) {
// associations can be defined here
};
return user;
};
user.js (controller):
const User = require('../models').user;
module.exports = {
getUser: function (req, res) {
User.findAll().then(value => {
res.json(value);
})
}
}
When I start the project, it return error Unhandled rejection SequelizeDatabaseError: relation "users" does not exist. As you can see my code above, I've set the model as user not users, and the table in db also user not users. It's only work fine if I add the tableName: 'user' in the model file.
NOTE: By the default, when I do create model with sequelize, the file name and model define is users, but I refactor file name and define inside model into user
Why does this happen?
This is default behavior - Sequelize automatically transforms model name to plural. In order to disable that you should freeze table name in model definition (or just set table name explicitly like you are actually doing):
freezeTableName: true,

Problem with Sequelize and specifying foreign key in an association

I have placements that belong to an App. The foreign_key on the placements table is appId (not AppId). I have specified as follows but get the error:
Unhandled rejection SequelizeDatabaseError: column Placements.AppId does not exist
My Placement Model:
'use strict';
const App = require('./').App
module.exports = (sequelize, DataTypes) => {
const Placement = sequelize.define('Placement', {
name: DataTypes.STRING,
isActive: DataTypes.BOOLEAN,
}, {
tableName: 'placements'
});
Placement.associate = function(models) {
Placement.belongsTo(models.App, { foreignKey: 'appId'})
// associations can be defined here
};
return Placement;
};
How do I tell Sequelize that the foreignKey is appId and not AppId?
Hmm... I don't see nothing wrong with your code. Which version of Sequelize are you using?
Just a couple of non-related comments:
You don't need to import App, it is already in models object.
I think you don't need tableName parameter either.
Try to use the field property
Placement.belongsTo(models.App, {
foreignKey: {
name: 'AppId',
field: 'appId'
}
}
)
You need to have the AppId columns present in App model. Explicitly passing { foreignKey: 'appId'}) to belongsTo makes Sequelize use the key as it is.
So you need to create an App model which has -
const App = sequelize.define('App', {
AppId: DataTypes.INTEGER,
..
}, {
tableName: 'App'
});
Another options would be to change this
Placement.belongsTo(models.App, { foreignKey: 'appId'})
to
Placement.belongsTo(models.App)
This way Sequelize automatically handles all relations between the models.

How to get model from sequelize.model after use sequelize-cli

I use sequelize-cli to auto generate code for model Student:
module.exports = (sequelize, DataTypes) => {
var _stu = sequelize.define('stu', {
name: DataTypes.STRING,
password: DataTypes.STRING,
gender: DataTypes.INTEGER,
}, {
classMethods: {
associate: function(models) {
// associations can be defined here
}
}
});
return _stu
};
My question are
How to get model named stu? As the sequelize is defined in function signature.
When sequelize model:generate,sequelize read config in config.json,where dbname,passowrd are specified.
How the sequelize in the function signature knows database connection config,say,name,password,etc as I don't specify in this js file.
Answer for question 1:
"sequelize.import" will do this job.
Try this code:
const Sequelize = require('sequelize')
const sequelize = new Sequelize(...)
const stu = sequelize.import('./models/stu')
stu.findAll.then(...)
When you generate models via the CLI, it creates a handy models/index.js file that handles passing in a Sequelize instance for you. You can simply require cherry picked, existing models via ES6 destructuring like this:
var { stu } = require("./models");
stu.findAll().then(...);
Alternatively, you could require them all at once, and then access specific models as needed:
var models = require("./models");
models.stu.findAll().then(...);
The way i make it works was importing model with the require() function and then call it with required parameters.
Explanation
By require function you will get another function that returns your model.
module.exports = (sequelize, DataTypes) => {
const stu = sequelize.define('stu', {
// ...database fields
}, {});
stu.associate = function(models) {
// associations can be defined here
};
return sty;
} // note that module.exports is a function
Now, this function requires the initialized sequelize object and a DataTypes object, so you just have to pass the instance of sequelize and Sequelize.DataTypes and then it will return your model, example:
const Sequelize = require('sequelize');
const sequelize = new Sequelize(...);
const Stu = require('./models/stu')(sequelize, Sequelize.DataTypes);
Finally you can get your database rows with Stu.findAll().
Hope this can help you.

Sequelize Association in class methods not working

I'm trying to set associations in class methods but it's not working as it should!
const User = sequelize.define('User', {
email: DataTypes.STRING,
password: DataTypes.STRING,
}, {
classMethods: {
associate: (models) => {
User.hasMany(models.AnotherModel, {foreignKey: 'userId'});
},
}
});
But when i set associations outside of classMethods block it works:
User.associate = function (models) {
User.hasMany(models.AnotherModel, {foreignKey: 'userId'});
};
why the codes inside classMethods block not working?
sequelize version: 4.2.0
This is an expected behavior, the classmethod syntax refers to sequelize version < 4.
Since v4, sequelize has evolved more towards js class syntax and hence the change.
The upgrade manual here details more about it
with "sequelize": "5".
Used this on my code to develop OSE server in Peru:
'use strict';
module.exports = (sequelize, DataTypes) => {
const UserModel = sequelize.define('user', {
user: DataTypes.STRING,
password: DataTypes.STRING,
description: DataTypes.STRING
}, {});
UserModel.associate = function (models) {
// associations can be defined here
};
UserModel['getHashName'] = () => {
return 'UserCache';
};
UserModel['getCacheKey'] = () => {
return 'user';
};
UserModel['getCacheValue'] = () => {
return 'password';
};
return UserModel;
};
and well, the next call :
console.log(User.getHashName());
console.log(User.getCacheKey());
console.log(User.getCacheValue());
Remember when library owners would leave the old method in place for a period of time, have it report that it's deprecated, but still operate so when we revisit a three year old project, and have zero idea why bringing the code forward breaks the entire universe, we might could get some direction on a resolution? I remember those days. Heady, wonderful days, those.
We're in the middle of a large code-forward update, bringing an old application to the modern versions of Node, et al, and holy crap, everything is just flat out broken. This'll be fun.
Digging in, this is "expected behavior", but wow, what a horrible way to learn about it. :(

Resources