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
} );
}
Related
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.
I don't understand this error (SequelizeForeignKeyConstraintError: insert or update on table \"segment\" violates foreign key constraint \"segment_id_fkey\"). I've read other answers on this topic on StackOverflow, but nothing that has pointed me in the right direction.
What I'm trying to do is have a user submit a form with some data that's then saved as a Segment model. The Segment model looks like so:
module.exports = (sequelize, DataTypes) => {
const Segment = sequelize.define(
'segment',
{
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true
},
provider_id: {
type: DataTypes.INTEGER,
references: {
model: 'provider',
key: 'id'
}
},
summary_id: {
type: DataTypes.INTEGER,
references: {
model: 'summary',
key: 'id'
}
},
audience_id: {
type: DataTypes.INTEGER,
references: {
model: 'audience',
key: 'id'
}
},
onboarding_id: {
type: DataTypes.INTEGER,
references: {
model: 'onboarding',
key: 'id'
}
}
},
{
// disable the modification of table names; By default, sequelize will automatically
// transform all passed model names (first parameter of define) into plural.
// if you don't want that, set the following
freezeTableName: true,
tableName: 'segment'
}
);
Segment.associate = models => {
Segment.belongsTo(models.Provider, { foreignKey: 'id' });
Segment.belongsTo(models.Summary, { foreignKey: 'id' });
Segment.belongsTo(models.Audience, { foreignKey: 'id' });
Segment.belongsTo(models.Onboarding, { foreignKey: 'id' });
};
return Segment;
};
The models that the Segment refers to (ie Provider, Summary, Audience, Onboarding), have a hasMany relationship to the Segment model. Something like so:
Provider.associate = models => {
Provider.hasMany(models.Segment, { foreignKey: 'provider_id' });
};
I have a feeling that the error has to do with the way I'm associating my models.
Any and all help is super appreciated! Thanks in advance!
Keep associations like:
Segment.associate = models => {
Segment.belongsTo(models.Provider, { foreignKey: 'provider_id' });
Segment.belongsTo(models.Summary, { foreignKey: 'summary_id' });
Segment.belongsTo(models.Audience, { foreignKey: 'audience_id' });
Segment.belongsTo(models.Onboarding, { foreignKey: 'onboarding_id' });
}
and in Providers, summary, Audience and oboarding files:
Providers.associate = models => {
Providers.belongsTo(models.Segments, { foreignKey: 'id' });
};
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
I want INNER JOIN with sequelize db: postgress
I have 2 database(users and posts)
Users
import Sequelize from "sequelize";
import { sequelize } from "../databases/database";
import Post from "./Post";
const User = sequelize.define(
"user",
{
id: {
type: Sequelize.INTEGER,
autoIncrement: true,
primaryKey: true
},
username: {
type: Sequelize.STRING,
},
password: {
type: Sequelize.STRING
},
image: {
type: Sequelize.STRING
}
},
{ timestamps: false }
);
User.hasMany(Post, { foreignKey: "author", sourceKey: "username" });
Post.belongsTo(User, { foreignKey: "author", sourceKey: "username" });
export default User;
Posts
import Sequelize from "sequelize";
import { sequelize } from "../databases/database";
const Post = sequelize.define("post", {
id: {
type: Sequelize.INTEGER,
primaryKey: true
},
title: {
type: Sequelize.TEXT
},
content: {
type: Sequelize.TEXT
},
image: {
type: Sequelize.STRING
},
description: {
type: Sequelize.TEXT
},
tags: {
type: Sequelize.STRING
},
author: {
type: Sequelize.STRING
},
createdAt: {
field: 'createdat',
type: Sequelize.DATE
},
updatedAt: {
field: 'updatedat',
type: Sequelize.DATE
}
});
export default Post;
Controller
export const listAllPosts = async params => {
const { offset } = params;
try {
const allPosts = await Post.findAll({
// limit: 20,
// offset: offset ? offset * 20 : 0,
// order: [["id", "DESC"]],
attributes: ["title", "content","tags","description","createdAt","updatedAt"],
required: true,
include:[
{
model:User,
attributes: ["username", "image"],
required:true,
}
]
});
return allPosts;
} catch (error) {
throw error;
}
};
and when i collect api show error:
Executing (default): SELECT "post"."id", "post"."title",
"post"."content", "post"."tags", "post"."description",
"post"."createdat"AS "createdAt", "post"."updatedat" AS "updatedAt",
"user"."id" AS "user.id", "user"."username" AS "user.username",
"user"."image" AS "user.image" FROM "posts" AS "post" INNER JOIN
"users" AS "user" ON "post"."author" = "user"."id";
error: SequelizeDatabaseError: operator does not exist: text = integer
But i want show exact and i dont want change primaryKey:
SELECT "post"."title", "post"."content", "post"."tags", "post"."description", "post"."createdat"AS "createdAt", "post"."updatedat" AS "updatedAt", "user"."username" AS "user.username", "user"."image" AS "user.image" FROM "posts" AS "post" INNER JOIN "users" AS "user" ON "post"."author" = "user"."username";
How i can do that?
You should better use 'userId' then 'author'. It'll fix your problem.
And edit association
User.hasMany(Post, { foreignKey: "userId", sourceKey: "id" });
Post.belongsTo(User, { foreignKey: "id", sourceKey: "userId" });
And advice you to read
https://www.essentialsql.com/get-ready-to-learn-sql-database-normalization-explained-in-simple-english/
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,
}]