Add data in Sequelize migration script? - node.js

How can I add data to a table in a Sequelize migration script?
This is what I got:
module.exports = {
up: function(migration, DataTypes, done) {
migration.createTable(
'person',
{
name: DataTypes.STRING,
age: DataTypes.INTEGER
},
{
charset: 'latin1' // default: null
}
);
// I want to insert person and age.
migration.insert(???);
done()
},
down: function(migration, DataTypes, done) {
migration.dropTable('queue');
done()
}
}

I figured it out. Sequelize is available from migration.migrator.sequelize. It is possible to do something like this:
up: function (migration, DataTypes, done) {
migration.createTable(
'Person',
{
name: DataTypes.STRING,
age: DataTypes.INTEGER,
}
).success(function () {
migration.migrator.sequelize.query("insert into person (name, age) values ('Donald Duck', 60)");
done();
});
},
down: function (migration, DataTypes, done) {
migration.dropTable(
'Person'
).then(function() {
done();
})
}

In the latest version of sequelize this has changed and it should now be
migration.createTable(
'Person',
{
name: DataTypes.STRING,
age: DataTypes.INTEGER,
}
).then(function () {
migration.sequelize.query("insert into person (name, age) values ('Donald Duck', 60)");
done();
});

Actually it is not a good idea to just run a query. queryInterface has create() and bulkCreate() now.
'use strict';
module.exports = {
up: function(queryInterface, Sequelize) {
return queryInterface.bulkInsert('roles', [{
label: 'user',
createdAt: new Date(),
updatedAt: new Date()
}, {
label: 'admin',
createdAt: new Date(),
updatedAt: new Date()
}]);
},
down: function(queryInterface, Sequelize) {
return queryInterface.bulkDelete('roles', null, {});
}
};
This returns a promise as expected by sequelize-cli.
Source (adapted): https://github.com/sequelize/sequelize/issues/3210

In sequelize version 4.41.2, when you use ES6+ and you want to do more complex insertions within migration you could make the up an async function which
creates your table and then inserts the necessary data into the table. To ensure that the migration either succeeds OR fails without making any changes a transaction is used for every
interaction with sequelize. Another important thing to note is that up must return a Promise.
Example of creating a table -> fetching data from other table -> modifying fetched data -> inserting modified data to new table:
module.exports = {
up: (queryInterface, Sequelize) => queryInterface.sequelize.transaction(async (transaction) => {
await queryInterface.createTable('person', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER,
},
name: {
type: Sequelize.STRING,
},
age: {
type: Sequelize.STRING,
},
}, { transaction });
// Returns array [[results], { /** result obj */ }]
const [dogs] = await queryInterface.sequelize.query('SELECT * FROM public."Dogs";', { transaction });
// prepare data
const metamorphed = dogs.map(({ name, age }) => ({
name,
age: parseInt((age * 7), 10),
}));
return queryInterface.bulkInsert('person', metamorphed, { transaction });
}),
down: queryInterface => queryInterface.dropTable('person'),
};

I'm Solution. this is an example:
"use strict";
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface
.createTable("roles", {
id: { type: Sequelize.INTEGER, autoIncrement: true, primaryKey: true },
name: { type: Sequelize.STRING },
description: { type: Sequelize.STRING },
})
.then(() => {
queryInterface.bulkInsert("roles", [{
name: "admin",
description: "Admins",
},
{
name: "company",
description: "Companies",
},
{
name: "user",
description: "Users",
},
]);
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable("roles");
},
};

Related

SequelizeDatabaseError: column User.last_sign_in_at does not exist. Column shows up in PGAdmin

I have recently tried to add the column "last_sign_in_at" to the User table (named "UsersTable" in our db) using sequelize. I've created the migration file and run the migration:
module.exports = {
async up (queryInterface, Sequelize) {
return queryInterface.addColumn('UsersTable', 'last_sign_in_at', {
type: Sequelize.DATE,
});
},
async down (queryInterface, Sequelize) {
return queryInterface.removeColumn('UsersTable', 'last_sign_in_at');
}
};
I have updated the User model in Node.js:
module.exports = (sequelize, DataTypes) => {
let User = sequelize.define(
'User',
{
firstName: DataTypes.STRING,
middleName: DataTypes.STRING,
lastName: DataTypes.STRING,
verificationKey: DataTypes.STRING,
verifiedDate: DataTypes.DATE,
LocationId: DataTypes.INTEGER,
...
gender: DataTypes.INTEGER,
ethnicity: DataTypes.INTEGER,
metro_location_id: DataTypes.INTEGER,
newToSalesType: {
type: DataTypes.INTEGER,
field: 'new_to_sales_type_id',
},
newToSalesStatus: {
type: DataTypes.INTEGER,
field: 'new_to_sales_status_id',
},
countryId: {
type: DataTypes.INTEGER,
field: 'country_id',
},
lastSignInAt: {
type: DataTypes.DATE,
field: 'last_sign_in_at',
}
},
I have a route that is supposed to update this value every time a user logs in and is authenticated:
router.authenticate = (req, res, next) => {
passport.authenticate('local', function (err, user, info) {
if (err) {
return set403Response(res);
}
if (!user) {
models.User.findAll({
where: {
email: {$ilike: req.body.email}
}
}).then(users => {
if (users.length > 0) {
if (!users[0].dataValues.isLinkedinLogin && !users[0].dataValues.isFacebookLogin) {
return set403Response(res, {emailExists: true});
}
}
return set403Response(res);
});
} else {
req.logIn(user, function (err) {
if (err) {
return set403Response(res);
}
models.UserLogin.write(user);
models.User.update(
{ lastSignInAt: Date.now() },
{ where: { user_id: user.user_id } }
).then(()=>{
console.log('TRACE 3')
return res.redirect('/api/currentUser');
})
});
}
})(req, res, next);
}
The field is showing up in PGAdmin:
last_sign_in_at in the db
I'm even able to make a SQL query to update the value of last_sign_in_at in my Query Tool for individual rows.
HOWEVER! When I try to log in a user in my app, my server is telling me:
SequelizeDatabaseError: column User.last_sign_in_at does not exist
I'm completely stumped.

Can't update a specific column in Sequelize

I have a model. It is for the intermediate(pivot) table.
UserCars.init({
carId: DataTypes.INTEGER,
userId: DataTypes.INTEGER,
title: DataTypes.STRING,
}, {
timestamps: false,
sequelize,
modelName: 'UserCars',
});
and here is my migration for this:
'use strict';
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable('UserCars', {
carId: {
allowNull: false,
type: Sequelize.INTEGER,
references: {
model: 'Cars',
key: 'id'
},
},
userId: {
allowNull: false,
type: Sequelize.INTEGER,
references: {
model: 'Users',
key: 'id'
},
},
title: {
type: Sequelize.STRING
},
}, {
uniqueKeys: {
Items_unique: {
fields: ['carId', 'userId']
}
}
});
},
down: async (queryInterface, Sequelize) => {
await queryInterface.dropTable('UserCars');
}
};
And I'm doing this below to create/update it:
userCar = await UserCars.findOne({
where: {
carId: 10,
userId: req.user.id,
}
});
if(userCar) {
userCar.userId = 20; // <--- This doesn't change
userCar.title = 'some other thing'; // <--- This changes
await userCar.save();
} else {
userCar = await UserCars.create({
userId: 20,
title: 'something'
});
}
The problem is, the title is being updated but the userId is not.
(I believe) this is due to constraints, you cannot use instance method to update FK value.
You need to use M-N association functions, or otherwise you could use raw SQL.
const car = await Car.findByPk(10);
const user = await User.findByPk(newValue);
// This also takes care of deleting the old associations
await car.setUsers(user, {
through: {'title': 'new value'}
});
I hope the upsert function is implemented in the future.
ref: https://github.com/sequelize/sequelize/issues/11836

Sequelize not generating join table or get/add methods

I run node 10.16.3 and sequelize 5.19.5. I created two models with a many to many relationship between them, using sequelize model:generate. It created both model and migration files. Supposedly, when I specify a many to many association, sequelize should generate the join table by itself, and also generate add methods on those models, so I can associate them properly. None of those things happened in my case after I ran sequelize db:migrate and sequelize db:seed:all. I saw many people just creating a join table manually, but I'd like to avoid that if there's a simpler way. Code files follow (with omitted imports for some constants that are irrelevant):
model Activity
module.exports = (sequelize, DataTypes) => {
const Activity = sequelize.define('Activity', {
name: DataTypes.ENUM(cleaningActivity),
category: DataTypes.STRING,
baseRate: DataTypes.FLOAT,
specialEquipment: DataTypes.STRING,
description: DataTypes.STRING,
deleted: DataTypes.BOOLEAN
}, {});
Activity.associate = function(models) {
Activity.belongsToMany(models.ActivityBundle, {
through: 'Activity_ActivityBundle'
});
};
return Activity;
};
Activity migration:
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Activity', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
name: {
type: Sequelize.ENUM(cleaningActivity)
},
category: {
type: Sequelize.STRING
},
baseRate: {
type: Sequelize.FLOAT
},
specialEquipment: {
type: Sequelize.STRING
},
description: {
type: Sequelize.STRING
},
deleted: {
type: Sequelize.BOOLEAN
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Activity');
}
};
Now, ActivityBundle model:
module.exports = (sequelize, DataTypes) => {
const ActivityBundle = sequelize.define('ActivityBundle', {
name: DataTypes.ENUM(cleaningBundle),
deleted: DataTypes.BOOLEAN
}, {});
ActivityBundle.associate = function(models) {
ActivityBundle.belongsToMany(models.Activity, {
through: 'Activity_ActivityBundle'
});
};
return ActivityBundle;
};
and finally, ActivityBundle migration:
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('ActivityBundle', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
name: {
type: Sequelize.ENUM(cleaningBundle)
},
deleted: {
type: Sequelize.BOOLEAN
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('ActivityBundle');
}
};
Then, I have a seed file, where I ma trying to first create bundles, then associate them to activities, like so:
module.exports = {
up: async (queryInterface, Sequelize) => {
const bundles = await ActivityBundle.bulkCreate(bundlesArray);
return Activity.bulkCreate(activitiesArray).then(activities => {
return Promise.all(activities.map(activity => {
const bundlesPerActivity = activityBundleMapping[activity.get('name')]
.map(name => bundles.find(b => b.get('name') === name));
return activity.addBundles(bundlesPerActivity); // this method does not exist, even though it should
}));
})
},
down: (queryInterface, Sequelize) => {
}
};
Clearly I am doing something wrong. What more am I supposed to define and where? I guess the migration files need to have some mention of many to many association? Not a clue, and official documentation is incomplete imho.
You need to generate the join table yourself. Sequelize isn't that magical. :)
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable(
'Activity_ActivityBundle',
{
createdAt: {
allowNull: false,
type: Sequelize.DATE,
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE,
},
ActivityId: {
type: Sequelize.INTEGER,
primaryKey: true,
},
ActivityBundleId: {
type: Sequelize.INTEGER,
primaryKey: true,
},
}
);
},
down: (queryInterface, Sequelize) => {
// remove table
return queryInterface.dropTable('Activity_ActivityBundle');
},
};

Multiple migration statements in one migration file

I am trying to execute multiple migration statements in a single migration file in order to make changes to multiple columns of same table in one go.
I want to know that whether I am doing it in a write way or not or is there a better and more appropriate way to do it:
Migration Code
module.exports = {
up: function(queryInterface, Sequelize, done) {
queryInterface.changeColumn('users', 'name', {
type: Sequelize.STRING,
allowNull: false,
require: true,
unique: true
}).success(function() {
queryInterface.changeColumn('users', 'address', {
type: Sequelize.STRING,
allowNull: false,
require: true,
unique: true
}).success(function() {
queryInterface.changeColumn('users', 'city', {
type: Sequelize.STRING,
allowNull: false,
require: true,
unique: true
}).success(function() {
queryInterface.changeColumn('users', 'state', {
type: Sequelize.STRING,
allowNull: false,
require: true,
defaultValue: "ncjnbcb"
});
done();
});
});
});
}
};
But I face an error which says:
TypeError: undefined is not a function
Since i couldn't find any way of debugging error in migrations, it will be great if someone helps me out in resolving it or if possible, tell about the way as of how can we figure out the errors in a migration.
Your TypeError is probably because you're not returning anything. The docs say that each migration function should return a Promise. No mention of a done callback.
To that end, try the following:
return Promise.all([
queryInterface.changeColumn...,
queryInterface.changeColumn...
]);
Using Promise.all with transactions (safer migration) :
module.exports = {
up: async (queryInterface, Sequelize) => {
return queryInterface.sequelize.transaction(t => {
return Promise.all([
queryInterface.changeColumn('users', 'name',
{ type: Sequelize.STRING },
{ transaction: t }
),
queryInterface.changeColumn('users', 'address',
{ type: Sequelize.STRING },
{ transaction: t }
),
queryInterface.changeColumn('users', 'city',
{ type: Sequelize.STRING },
{ transaction: t }
)
]);
});
},
down: async (queryInterface, Sequelize) => {
return queryInterface.sequelize.transaction((t) => {
return Promise.all([
queryInterface.removeColumn('users', 'name', { transaction: t }),
queryInterface.removeColumn('users', 'address', { transaction: t }),
queryInterface.removeColumn('users', 'city', { transaction: t })
])
})
}
};
Using Promise.all without transactions would cause issues if some of the queries are rejected. It is safe to use transactions so that all operations would be executed successfully or none of the changes would be made.
module.exports = {
up: async (queryInterface, Sequelize) => {
try {
await queryInterface.addColumn('User', 'name', {
type: Sequelize.STRING
});
await queryInterface.addColumn('User', 'nickname', {
type: Sequelize.STRING
});
return Promise.resolve();
} catch (e) {
return Promise.reject(e);
}
},
down: async (queryInterface, Sequelize) => {
try {
await queryInterface.removeColumn('Challenges', 'name');
await queryInterface.removeColumn('Challenges', 'nickname');
return Promise.resolve();
} catch (e) {
return Promise.reject(e);
}
}
};
So this is a combination of 2 answers.
#Firmino Changani - Works great but will complete some of the migrations even if some fail
#Aswin Sanakan - Means they all work or none migrate but if the second migration depends on the first it will not work
I was creating a table and adding a special index to that table. So I ended up combining them and the below works for me:
'use strict';
module.exports = {
up: async (queryInterface, Sequelize) => {
return queryInterface.sequelize.transaction(async t => {
try {
await queryInterface.createTable(
'phonenumbers',
{
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
full_number: {
type: Sequelize.STRING,
unique: true
},
phone: {
type: Sequelize.STRING
},
extension: {
type: Sequelize.INTEGER,
},
country_id: {
type: Sequelize.INTEGER
},
is_valid_format: {
type: Sequelize.BOOLEAN
},
type: {
type: Sequelize.STRING
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
},
},
{ transaction: t }
),
await queryInterface.addIndex(
'phonenumbers',
['phone'],
{
name: 'constraint-phone-extension',
where: {extension: null},
transaction: t
}
)
return Promise.resolve();
} catch (e) {
return Promise.reject(e);
}
});
},
down: async (queryInterface, Sequelize) => {
return queryInterface.sequelize.transaction(async t => {
try {
await queryInterface.dropTable('phonenumbers', { transaction: t }),
return Promise.resolve();
} catch (e) {
return Promise.reject(e);
}
})
}
};

Node Sequelize migrations/models is it possible to share the same code?

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');
}
};

Resources