Error when running migration on sequelize - postgresql

'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return Promise.all([
queryInterface.addColumn('Posts', 'userAccountId', {
type: Sequelize.INTEGER,
}),
queryInterface.addColumn('Posts', 'postTopicId', {
type: Sequelize.INTEGER,
}),
queryInterface.addConstraint('Posts', ['userAccountId'], {
type: 'foreign key',
name: 'userAccountId',
references: {
table: 'UserAccounts',
field: 'id',
},
onDelete: 'no action',
onUpdate: 'no action',
}),
queryInterface.addConstraint('Posts', ['postTopicId'], {
type: 'foreign key',
name: 'postTopicId',
references: {
table: 'PostTopics',
field: 'id',
},
onDelete: 'no action',
onUpdate: 'no action',
}),
]);
},
down: (queryInterface, Sequelize) => {
return Promise.all([
queryInterface.removeColumn('Posts', 'userAccountId'),
queryInterface.removeColumn('Posts', 'postTopicId'),
queryInterface.removeConstraint('Posts', 'userAccountId'),
queryInterface.removeConstraint('Posts', 'postTopicId'),
]);
},
};
When I run the command npx sequelize db:migrate, I receive this error "ERROR: column "postTopicId" referenced in foreign key constraint does not exist"
I dont what is wrong, all the other migrations running ok.
I'm running my database in a docker container.

You should not use Promise.all while executing queries that modifying structure and depending on each other. Promise.all does not guarantee an original order of execution queries. Use transaction and sequential execution:
return queryInterface.sequelize.transaction(async transaction => {
await queryInterface.addColumn('Posts', 'userAccountId', {
type: Sequelize.INTEGER,
}, { transaction })
await queryInterface.addColumn('Posts', 'postTopicId', {
type: Sequelize.INTEGER,
}, { transaction })
await queryInterface.addConstraint('Posts', ['userAccountId'], {
type: 'foreign key',
name: 'userAccountId',
references: {
table: 'UserAccounts',
field: 'id',
},
onDelete: 'no action',
onUpdate: 'no action',
}, { transaction })
await queryInterface.addConstraint('Posts', ['postTopicId'], {
type: 'foreign key',
name: 'postTopicId',
references: {
table: 'PostTopics',
field: 'id',
},
onDelete: 'no action',
onUpdate: 'no action',
}, { transaction })
});

Related

Postgres: SequelizeDatabseError: column id does not exist

I am using Postgresql with code-first approach in my Node.JS project. Some of the tables were already there in the inherited code. There is a table called user_games storing the information about which user is playing which games. Model of the table has been defined as follows:
'use strict';
const {
Model
} = require( 'sequelize' );
module.exports = ( sequelize, DataTypes ) => {
class UserGames 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
UserGames.belongsTo( models.Game, {
as: 'Games',
foreignKey: 'game_id',
onDelete: 'NO ACTION',
onUpdate: 'NO ACTION'
} );
UserGames.belongsTo( models.User, {
as: 'User',
foreignKey: 'user_id',
onDelete: 'NO ACTION',
onUpdate: 'NO ACTION'
} );
UserGames.hasMany( models.GameUrl, {
foreignKey: 'game_id'
} );
}
}
UserGames.init( {
user_game_id: { // eslint-disable-line camelcase
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true,
allowNull: false
},
user_id: DataTypes.STRING, // eslint-disable-line camelcase
games_gamer_id: DataTypes.STRING, // eslint-disable-line camelcase
game_id: DataTypes.UUID // eslint-disable-line camelcase
}, {
sequelize,
createdAt: 'createdAt',
updatedAt: 'updatedAt',
modelName: 'UserGames',
tableName: 'user_games',
} );
return UserGames;
};
Things were working fine but suddenly the following (and other related) code started complaining "column UserGames.id does not exist" :
models.Game.findAndCountAll( {
include: [ {
model: models.UserGames,
as: 'UserGames',
where: { user_id: userId }, // eslint-disable-line camelcase
} ],
attributes: [ 'name', 'description_text', 'icon_url' ],
order: [
[ 'name', 'ASC' ]
],
} );
So, we had to modify 'include' part for the table user_games to specify the attributes explicitly as below:
include: [ {
model: models.UserGames,
as: 'UserGames',
where: { user_id: userId }, // eslint-disable-line camelcase
attributes: [ 'game_id' ], //otherwise it will look for 'id' field also.
} ],
As shown in the model definition, the table contains a column user_game_id as the primary key and as I mentioned, things were working fine a few days ago. So, I am not able to understand the reason for the error. Any suggestion will be highly appreciated.
Edit: Here is the game model:
/* eslint-disable camelcase */
'use strict';
const {
Model
} = require( 'sequelize' );
module.exports = ( sequelize, DataTypes ) => {
class Game 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
Game.belongsTo( models.Partner, {
as: 'Partner',
foreignKey: 'partner_id',
onDelete: 'CASCADE',
onUpdate: 'CASCADE'
} );
Game.hasMany( models.UserGames, {
as: 'UserGames',
foreignKey: 'game_id',
onDelete: 'CASCADE',
onUpdate: 'CASCADE'
} );
Game.hasMany( models.GameAsset, {
as: 'GameAssetDetails',
foreignKey: 'game_id',
onDelete: 'CASCADE',
onUpdate: 'CASCADE'
} );
Game.hasMany( models.GameUrl, {
as: 'GameUrlDetails',
foreignKey: 'game_id',
onDelete: 'CASCADE',
onUpdate: 'CASCADE'
} );
Game.hasMany( models.GameTestingComment, {
foreignKey: 'game_id',
onDelete: 'CASCADE',
onUpdate: 'CASCADE'
} );
Game.hasMany( models.OfferSchedule, {
foreignKey: 'game_id'
} );
}
}
Game.init( {
game_id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true,
allowNull: false
},
partner_id: {
type: DataTypes.UUID,
allowNull: false
},
name: DataTypes.STRING,
description: DataTypes.TEXT,
description_text: DataTypes.TEXT,
icon_url: DataTypes.STRING,
status: {
type: DataTypes.ENUM,
values: [ "1", "2", "3", "4" ],
defaultValue: "1",
comment: "1 => 'In Development', 2 => 'Submit for Approval', 3 => 'Approved', 4 => 'Rejected'",
},
approved_date: {
allowNull: true,
type: DataTypes.DATE
},
submitted_date: {
allowNull: true,
type: DataTypes.DATE
},
is_active: {
type: DataTypes.BOOLEAN,
defaultValue: true,
allowNull: false
}
}, {
sequelize,
createdAt: 'createdAt',
updatedAt: 'updatedAt',
modelName: 'Game',
tableName: 'game',
} );
return Game;
};
Ultimately, we are able to figure out the source of issue. A few days back, one of the developers in the team added the wrong association. He set 'id' column of UserGames as foreign key in other table though the UserGames did not have 'id' column at all. Below is the faulty code:
static associate( models ) {
GameProducts.hasMany( models.UserGames, {
as: 'SubscriptionPlans',
sourceKey: "game_product_id",
foreignKey: 'id' //Faulty code. UserGames does not have 'id' column
} );
}

SequelizeDatabaseError: the operator does not exist : integer = boolean when using special methods on model instance

I have two models in Many-to-Many association : Agency and Customer.
UPDATE : (here are the models and association definition)
Agency :
const agency = (sequelize, DataTypes) => {
const Agency = sequelize.define('agency', {
name: {
type: DataTypes.STRING,
allowNull: false,
validate: {
notEmpty: true,
}
},
});
Agency.associate = models => {
Agency.hasMany(models.User, { onDelete: 'CASCADE' });
Agency.belongsToMany(models.Customer, { onDelete: 'SET NULL', through: 'CustomerAgencies'});
};
return Agency;
};
export default agency;
Customer :
const customer = (sequelize, DataTypes) => {
const Customer = sequelize.define('customer', {
email: {
type: DataTypes.STRING,
unique: true,
allowNull: false,
validate: {
notEmpty: true,
isEmail: true,
},
},
firstname: {
type: DataTypes.STRING,
allowNull: true,
validate: {
notEmpty: true,
}
},
lastname: {
type: DataTypes.STRING,
allowNull: true,
validate: {
notEmpty: true,
}
},
phone: {
type: DataTypes.INTEGER,
},
language: {
type: DataTypes.STRING,
},
});
Customer.associate = models => {
Customer.belongsToMany(models.Agency, { onDelete: 'SET NULL', through: 'CustomerAgencies'});
};
return Customer;
};
export default customer;
When i create some entries, there is no problem :
const agency = await models.Agency.findOrCreate({
where: { name: 'SunTour'},
defaults: {
name: 'SunTour'
}
});
const customer = await models.Customer.findOrCreate({
where: { email: 'paulo#example.com' },
defaults: {
email: 'paulo#example.com',
firstname: 'Paulo',
lastname: 'Dena',
phone: '0202020202',
},
});
But i'm trying to use the special methods of Sequelize that are available when two models are in relation.
Juste under the previous code example i wrote this to check is there is already an assocation between the two instances :
debug(await customer[0].hasAgency(agency));
The node server return me the error below for this line :
UnhandledPromiseRejectionWarning: SequelizeDatabaseError: the operator does not exist : integer = boolean
In the console the query executed is this one :
SELECT "agency"."id" FROM "agencies" AS "agency" INNER JOIN "CustomerAgencies" AS "CustomerAgencies" ON "agency"."id" = "CustomerAgencies"."agencyId" AND "CustomerAgencies"."customerId" = 1 WHERE ((("agency"."id" = 1 OR "agency"."id" = false)));
You can see that Sequelize query is using "agency.id = false" in his condition, i guess that's the problem.
Note : i'm using a PostGreSQL Database.
I guess the problem come from the PostGreSQL Database, but what can i do ?
Thanks in advance.

How do I get sequelize to not use timestamp fields when generating SQL for getting n:m associated models?

My models:
'use strict';
module.exports = (sequelize, DataTypes) => {
const Vendor = sequelize.define('Vendor', {
id: { type: DataTypes.INTEGER, allowNull: false, autoIncrement: true, primaryKey: true },
// other fields
}, {});
Vendor.associate = function (models) {
Vendor.belongsToMany(models.Corporate, { through: 'VendorCorporates', foreignKey: 'vendorId' });
};
return Vendor;
};
and
'use strict';
module.exports = (sequelize, DataTypes) => {
const Corporate = sequelize.define('Corporate', {
id: { type: DataTypes.INTEGER, allowNull: false, autoIncrement: true, primaryKey: true },
// other fields
}, {});
Corporate.associate = function (models) {
Corporate.belongsToMany(models.Vendor, { through: 'VendorCorporates', foreignKey: 'corporateId' });
};
return Corporate;
};
and the migration for the association (link) table:
'use strict';
const vendorCorps = 'VendorCorporates', vendorId = 'vendorId', corpId = 'corporateId';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable(vendorCorps, {
vendorId: {
type: Sequelize.INTEGER,
references: {
model: 'Vendors',
key: 'id'
},
onUpdate: 'CASCADE',
onDelete: 'CASCADE'
},
corporateId: {
type: Sequelize.INTEGER,
references: {
model: 'Corporates',
key: 'id'
},
onUpdate: 'CASCADE',
onDelete: 'CASCADE'
},
// notice no timestamps
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('VendorCorporates');
}
};
Now when I try to get the Corporates that are associated with a Vendor:
let vendor = await models.Vendor.findByPk(pk);
let corporates = await vendor.getCorporates();
the query fails because sequelize generates SQL with the fields updatedAt and createdAt. How do I tell sequelize to generate SQL without the timestamp fields?
Clarification: I want timestamps in the Corporate & Vendor models, but none in the link/association table, and I want the join query (vendor.getCorporates()) to be generated accordingly.
You can indicate this using the timestamps option. For instance:
const Corporate = sequelize.define('Corporate', {
id: { type: DataTypes.INTEGER, allowNull: false, autoIncrement: true, primaryKey: true },
// other fields
}, {
timestamps: false
});
Finally solved this by writing a model for the n:m association itself:
'use strict';
module.exports = (sequelize, DataTypes) => {
const VendorCorporate = sequelize.define('VendorCorporate', {
vendorId: DataTypes.INTEGER,
corporateId: DataTypes.INTEGER
}, { timestamps: false });
VendorCorporate.associate = function (models) {
// associations can be defined here
};
return VendorCorporate;
};
and changing:
// db/models/vendor.js
Vendor.belongsToMany(models.Corporate, { through: 'VendorCorporates', foreignKey: 'vendorId' });
to:
Vendor.belongsToMany(models.Corporate, { through: models.VendorCorporate, foreignKey: 'vendorId' });
and making a similar change in the Corporate model file.

Sequelize relation "likes" does not exist

I'm not understanding why sequelize is giving me this error.
relation "Likes" does not exist
I referenced a similar question, but it didn't provide me with much of an insight:
Sequelize Error: Relation does not exist
and this too:
Sequelize Migration: relation <table> does not exist
My table names matches the reference model names.
I don't think it has anything to do with the models, but everything to do with the migrations file.
This is what I have
Posts migration
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Posts', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
title: {
type: Sequelize.STRING
},
post_content: {
type: Sequelize.STRING
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
},
userId: {
type: Sequelize.INTEGER,
references: {
model: 'Users',
key: 'id'
}
},
likeId: {
type: Sequelize.INTEGER,
onDelete: 'CASCADE',
references: {
model: 'Likes',
key: 'id'
}
},
username: {
type: Sequelize.STRING
},
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Posts');
}
};
Likes migration
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Likes', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
like: {
type: Sequelize.BOOLEAN
},
userId: {
type: Sequelize.INTEGER,
references: {
model: 'Users',
key: 'id',
as: 'userId'
}
},
postId: {
type: Sequelize.INTEGER,
references: {
model: 'Posts',
key: 'id',
as: 'postId'
}
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Likes');
}
};
models/like.js
'use strict';
const Like = (sequelize, DataTypes) => {
const Likes = sequelize.define('Likes', {
like:{
type:DataTypes.BOOLEAN,
allowNull:true
}
}, {});
Likes.associate = function(models) {
Likes.belongsTo(models.User, {
onDelete: "CASCADE",
foreignKey: {
foreignKey: 'userId'
}
})
Likes.belongsTo(models.Post, {
onDelete: 'CASCADE',
foreignKey: 'likeId',
targetKey: 'id',
})
}
return Likes;
};
module.exports = Like;
models/post.js
module.exports = (sequelize, DataTypes) => {
const Post = sequelize.define('Post', {
title: DataTypes.STRING,
post_content: DataTypes.STRING,
username: DataTypes.STRING
}, {});
Post.associate = function(models) {
Post.belongsTo(models.User, { foreignKey: 'userId', targetKey: 'id' });
Post.belongsTo(models.Likes, { foreignKey: 'likeId', targetKey: 'id' });
};
return Post;
};
models/user.js
'use strict';
const User = (sequelize, DataTypes) => {
const myUser = sequelize.define('User', {
username: DataTypes.STRING,
email: DataTypes.STRING,
password: DataTypes.STRING,
resetPasswordToken:DataTypes.STRING,
resetPasswordExpires: DataTypes.DATE
}, {});
myUser.associate = function(models) {
myUser.hasMany(models.Post, { foreignKey: 'userId', as:'users' });
myUser.hasMany(models.Likes, { foreignKey: 'userId', as:'likes' });
};
return myUser;
};
module.exports = User;
Instead of adding likeId in the migration. I needed to add a new migration like so
sequelize migration:generate --name add_likeId_to_posts
so we have now
'use strict';
module.exports = {
up: function (queryInterface, Sequelize) {
return queryInterface.addColumn(
'Posts',
'likeId',
{
type: Sequelize.INTEGER,
allowNull: false,
references: {
model: 'Likes',
key: 'id'
}
}
)
},
down: function (queryInterface, Sequelize) {
return queryInterface.removeColumn(
'Posts',
'likeId'
)
}
};
which gives us
voila!
and likeId is Associated on the table

Postgres Include throws "name": "SequelizeEagerLoadingError"

I am having trouble figuring out why my include statement throws this error
"name": "SequelizeEagerLoadingError"
My controller function in question looks like this
retrieve (req, res) {
return User
.findOne({
where: {
token_id: req.params.token_id
},
include: [{
model: subscribedcurrency,
as: 'subscribed currency'
}]
})
.then(user => {
if (!user) {
return res.status(404).send({
message: 'User Not Found'
})
}
return res.status(200).send(user)
})
.catch(error => res.status(400).send(error))
},
The model for subscribedcurrency looks like this
module.exports = (sequelize, DataTypes) => {
var SubscribedCurrency = sequelize.define('SubscribedCurrency', {
symbol: {
type: DataTypes.STRING,
allowNull: false
},
name: {
type: DataTypes.STRING,
allowNull: false
},
priceAtSubscription: {
type: DataTypes.STRING,
allowNull: false
}
})
SubscribedCurrency.associate = (models) => {
SubscribedCurrency.hasMany(models.User, {
foreignKey: 'userId',
onDelete: 'CASCADE'
})
}
return SubscribedCurrency
}
I've tried changing how it queries, the primary key of User, just about everything I can think of. The relationship between User and subscribedCurrency is many to many.
without the include statement and the findOne query works perfect fine!
I think all you need is to remove as: 'subscribed currency' , as you haven't define association with alias.
So From this :
include: [{
model: subscribedcurrency,
as: 'subscribed currency'
}]
To this :
include: [{
model: subscribedcurrency,
}]