Sequelize Model associations - foreign key missing - postgresql

I have 2 models that I am associating like this. Customer is associated to application by 1:M relationship.
customer:
'use strict';
module.exports = (sequelize, DataTypes) => {
let customer = sequelize.define('customer', {
id: {
type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true,
autoIncrement: true
},
name: {
type: DataTypes.STRING
},
account_id: {
type: DataTypes.STRING
},
code: {
type: DataTypes.STRING
},
createdAt: {
type: DataTypes.DATE,
defaultValue: sequelize.literal('NOW()')
},
updatedAt: {
type: DataTypes.DATE,
defaultValue: sequelize.literal('NOW()')
}
},
{
underscored: true,
freezeTableName: true,
tableName: 'customer'
});
customer.associate = function(models) {
// associations can be defined here
customer.hasMany(models.application, { foreignKey:
'customer_id' });
};
sequelize.sync()
.then(() => customer.create(
{ name: "customer1", account_id: "cust-1-acct-1", code: "ACME Inc." }
)).then(function(customer) {
console.log('customers created');
}).then(() => customer.create(
{ name: "customer2", account_id: "cust-2-acct-2", code: "test Cust" }
)).then(function(customer) {
console.log('customers created');
})
.catch(function(err) {
console.log(err);
});
return customer;
}
application:
'use strict';
module.exports = (sequelize, DataTypes) => {
let application = sequelize.define('application', {
id: {
type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true,
autoIncrement: true
},
name: {
type: DataTypes.STRING,
sortable: true
},
creation_date: {
type: DataTypes.NUMERIC,
sortable: true
},
customer_id: {
type: DataTypes.INTEGER
},
createdAt: {
type: DataTypes.DATE,
defaultValue: sequelize.literal('NOW()')
},
updatedAt: {
type: DataTypes.DATE,
defaultValue: sequelize.literal('NOW()')
}
},
{
underscored: true,
freezeTableName: true,
tableName: 'application'
});
application.associate = function(models) {
// associations can be defined here
application.belongsTo(models.customerView, { through: 'customer_id' });
};
sequelize.sync()
.then(() => application.create(
{ customer_id: "1", name: "application 1", creation_date: "1556724178700" }
)).then(() => application.create(
{ customer_id: "1", name: "application 2", creation_date: "1556724178700" }
)).then(() => application.create(
{ customer_id: "2", name: "application 3", creation_date: "1556724178700" }
))
.then(function(application) {
console.log('applications created');
})
.catch(function(err) {
console.log(err);
});
return application;
}
These 2 tables are getting created as expected, but without the foreign key constraint that I am expecting. The foreign key should be on the application table, on customer_id.
What am I doing wrong?

Related

Sequelize Migration addIndex not adding index in descending order

I am trying to create an index on an existing table (Postgres) on a date column so that I can get the latest posts first
Model file feeditem.js
module.exports = (sequelize, DataTypes) => {
const FeedItem = sequelize.define('FeedItem', {
feedItemId: {
//...
},
pubdate: {
allowNull: false,
type: DataTypes.DATE,
validate: {
isDate: true,
notEmpty: true,
},
},
link: {
//...
},
title: {
//...
},
description: {
//...
},
summary: {
//...
},
author: {
//...
},
hash: {
//...
},
}, {
timestamps: false,
underscored: true,
indexes: [
{
fields: [{ attribute: 'pubdate', order: 'DESC' }],
unique: false,
},
],
});
FeedItem.associate = (models) => {
// associations can be defined here
//...
};
return FeedItem;
};
Migration file create-feed-item.js
module.exports = {
up: (queryInterface, Sequelize) => queryInterface.createTable('feed_items', {
feed_item_id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER,
},
pubdate: {
allowNull: false,
type: Sequelize.DATE,
},
link: {
allowNull: false,
type: Sequelize.STRING,
},
title: {
allowNull: false,
type: Sequelize.STRING,
},
description: {
type: Sequelize.TEXT,
},
summary: {
type: Sequelize.TEXT,
},
author: {
type: Sequelize.STRING,
},
hash: {
allowNull: false,
type: Sequelize.UUID,
unique: true,
},
}),
// eslint-disable-next-line no-unused-vars
down: (queryInterface, Sequelize) => queryInterface.dropTable('feed_items'),
};
Migration file add-index.js
module.exports = {
/*
Add altering commands here.
Return a promise to correctly handle asynchronicity.
Example:
return queryInterface.createTable('users', { id: Sequelize.INTEGER });
*/
// eslint-disable-next-line no-unused-vars
up: (queryInterface, Sequelize) => queryInterface.addIndex('feed_items', ['pubdate'], {
fields: [{
attribute: 'pubdate', order: 'DESC',
}],
unique: false,
name: 'feed_items_pubdate_index',
}),
/*
Add reverting commands here.
Return a promise to correctly handle asynchronicity.
Example:
return queryInterface.dropTable('users');
*/
// eslint-disable-next-line no-unused-vars
down: (queryInterface, Sequelize) => queryInterface.removeIndex('feed_items', 'feed_items_pubdate_index'),
};
The migrations are run in perfect order where table is created first and index is added later.
Logging is enabled and when I check the logs
Expected:
It should create a DESC index on pubdate which goes like
CREATE INDEX "feed_items_pubdate_index" ON "feed_items" ("pubdate" DESC)
Actual Output
Executing (default): CREATE INDEX "feed_items_pubdate_index" ON "feed_items" ("pubdate")
Any ideas what is going wrong here?
Your question is still relevant because it is undocumented.
The proper way to define index in descending order is extended definition in fields argument.
So the migration file add-index.js should look like:
module.exports = {
...
up: (queryInterface, Sequelize) => queryInterface.addIndex(
'feed_items',
[{
attribute: 'pubdate', order: 'DESC',
}],
{
unique: false,
name: 'feed_items_pubdate_index',
}
),
...
};
Resolved issue reference.
use uniqueKeys:
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Users', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
name: {
allowNull: true,
type: Sequelize.STRING
},
order: {
allowNull: false,
type: Sequelize.INTEGER
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
},
{
uniqueKeys: {
actions_unique: {
fields: ["name", "order"],
},
},
}
);
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Users');
}
};

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

Sequelize Include doesn't work without results

I'm doing the select below and it works great when it has results to return, when I it doesn't i have an error like this
TypeError: Cannot read property 'id' of null
This only occcurs when they city doesn't have any marker, otherwise it works as expected. Did anyone have this problem? The error occurs with or wihout where clause
City.findById(req.params.id,{
include: [{ model: Marker, as: "markers",
where: {
status: '1'
},
}]
}).then(city =>{
console.log(city.id);
res.status(201).send(city);
}) .catch(error => {
console.log(error);
res.status(400).send(error)
});
City
module.exports = (sequelize, DataTypes) => {
const City = sequelize.define('city', {
name: { type: DataTypes.STRING, allowNull: false },
status: { type: DataTypes.INTEGER, allowNull: false },
latitude: { type: DataTypes.DECIMAL, allowNull: false },
longitude: { type: DataTypes.DECIMAL, allowNull: false },
}, { freezeTableName: true});
City.associate = function(models) {
// associations can be defined here
City.hasMany(models.marker,{as: 'markers', foreignKey: 'cityId'})
};
return City;
};
Marker
module.exports = (sequelize, DataTypes) => {
const Marker = sequelize.define('marker', {
description: { type: DataTypes.STRING, allowNull: false },
status: { type: DataTypes.INTEGER, allowNull: false },
latitude: { type: DataTypes.DECIMAL, allowNull: false },
longitude: { type: DataTypes.DECIMAL, allowNull: false },
cityId: {
type: DataTypes.INTEGER,
references: {
model: 'city',
key: 'id',
},
allowNull: false,
}
}, { freezeTableName: true});
Marker.associate = function(models) {
// associations can be defined here
};
return Marker;
};
query logged.
Executing (default): SELECT "city"."id", "city"."name", "city"."status", "city".
"latitude", "city"."longitude", "city"."createdAt", "city"."updatedAt", "markers
"."id" AS "markers.id", "markers"."description" AS "markers.description", "marke
rs"."status" AS "markers.status", "markers"."latitude" AS "markers.latitude", "m
arkers"."longitude" AS "markers.longitude", "markers"."cityId" AS "markers.cityI
d", "markers"."createdAt" AS "markers.createdAt", "markers"."updatedAt" AS "mark
ers.updatedAt" FROM "city" AS "city" INNER JOIN "marker" AS "markers" ON "city".
"id" = "markers"."cityId" AND "markers"."status" = '1' WHERE "city"."id" = '4';
It's because of this line:
console.log(city.id);
When city is null, which happens when the query doesn't match any documents in the database, your code evaluates to null.id, which is invalid, hence the error.

Sequelize Eager Loading Error when including related model

I'm using Sequelize to make this request:
return Expense.findAll({
include: [{
model: ExpenseCategory
}],
})
.then(expenses => res.status(200).send(expenses))
.catch(error => res.status(500).send({ error: error }));
and I'm getting this error:
SequelizeEagerLoadingError
I can't seem to find my error.
This are my migrations for the three models (User, Expense, ExpenseCategory):
queryInterface.createTable('Users', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
email: {
allowNull: false,
type: Sequelize.STRING,
unique: true
},
passhash: {
allowNull: false,
type: Sequelize.STRING
},
currency: {
type: Sequelize.STRING,
defualt: 'lev'
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
queryInterface.createTable('Expenses', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
value: {
allowNull: false,
type: Sequelize.FLOAT
},
text: {
type: Sequelize.STRING
},
expenseCategoryId: {
allowNull: false,
type: Sequelize.INTEGER,
references: {
model: 'ExpenseCategories',
key: 'id'
},
onDelete: 'cascade'
},
userId: {
allowNull: false,
type: Sequelize.INTEGER,
references: {
model: 'Users',
key: 'id'
},
onDelete: 'cascade'
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
queryInterface.createTable('ExpenseCategories', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
category: {
allowNull: false,
type: Sequelize.STRING
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
and the model definitions:
const User = sequelize.define('User', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: DataTypes.INTEGER
},
email: {
allowNull: false,
type: DataTypes.STRING,
unique: true
},
passhash: {
allowNull: false,
type: DataTypes.STRING
},
currency: {
type: DataTypes.STRING,
defaultValue: 'lev'
}
}, {
classMethods: {
associate: function (models) {
User.hasMany(models.Income, {
foreignKey: 'userId',
});
User.hasMany(models.Expense, {
foreignKey: 'userId',
});
}
}
});
const Expense = sequelize.define('Expense', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: DataTypes.INTEGER
},
value: {
allowNull: false,
type: DataTypes.FLOAT
},
text: {
type: DataTypes.STRING
},
expenseCategoryId: {
allowNull: false,
type: DataTypes.INTEGER
},
userId: {
allowNull: false,
type: DataTypes.INTEGER
}
}, {
classMethods: {
associate: function (models) {
Expense.belongsTo(models.User, {
foreignKey: 'userId'
});
Expense.belongsTo(models.ExpenseCateogory, {
foreignKey: 'expenseCateogoryId',
});
}
}
});
const ExpenseCategory = sequelize.define('ExpenseCategory', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: DataTypes.INTEGER
},
category: {
allowNull: false,
type: DataTypes.STRING
}
}, {
classMethods: {
associate: function (models) {
ExpenseCateogory.hasMany(models.Expense, {
foreignKey: 'expenseCategoryId'
});
}
}
});
I got an working answer. In this example i have a scheme where a department can have a lot of positions. The Position will include the department and the department will include its positions.
models/Department.js
module.exports = (sequelize, DataTypes) =>
{
const Sequelize = require('sequelize');
const Department = sequelize.define('Department',
{
...
}
Department.associate = function(models) {
Department.hasMany(models.Position, {
foreignKey: 'department_id',
as: 'positions'
});
};
return Department;
};
models/Position.js
module.exports = (sequelize, DataTypes) =>
{
const Sequelize = require('sequelize');
const Position = sequelize.define('Position',
{
...
}
Position.associate = function(models) {
Position.belongsTo(models.Department, {
foreignKey: 'department_id',
as: 'department',
onDelete: 'CASCADE'
});
};
return Position;
};
controllers/departmentController.js
exports.all = async function(req, res)
{
return Department
.findAll({include: [ 'positions' ]})
.then((data) => {
if (!data) { return res.status(400).json({status: 400,message: 'Registro não encontrado', data: data }); }
return res.status(200).json(data);
})
.catch((error) => {
return res.status(400).json({message: 'Falha no banco de dados.', data: error})
});
};
controllers/positionController.js
exports.all = async function(req, res)
{
return Position
.findAll({include: [ 'department' ]})
.then((data) => {
if (!data) { return res.status(400).json({status: 400,message: 'Registro não encontrado', data: data }); }
return res.status(200).json(data);
})
.catch((error) => {
console.log(error);
return res.status(400).json({message: 'Falha no banco de dados.', data: error})
});
};
Just change this sections
classMethods: {
associate: function (models) {
ExpenseCateogory.hasMany(models.Expense, {
foreignKey: 'expenseCategoryId'
});
}
}
to
ExpenseCategory.associate = (models) => {
ExpenseCategory.hasMany(models.style,{
as:'expensecategories'
});
}
so the model in full will follow this structure and the other models as well
const ExpenseCategory = sequelize.define('ExpenseCategory', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: DataTypes.INTEGER
},
category: {
allowNull: false,
type: DataTypes.STRING
}
});
ExpenseCategory.associate = (models) => {
ExpenseCategory.hasMany(models.style,{
as:'expensecategories'
});
}
return ExpenseCategory;
This is with reference to this youtube video https://www.youtube.com/watch?v=SaVxJrTRkrI and this example from github for sequelize examples on associations for models https://github.com/sequelize/express-example/tree/master/models

Eager Loading : How to disable specific fields of included table

I am trying to do Eager Loading in Sequelize with PostgreSQL where I need to find the Users which have a given specific Mail id or basically, i am performing find operation on Mail model while using include to include User model
UserModel :
module.exports = function (sequelize, Sequelize) {
var User = sequelize.define('User', {
userId: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
firstname: {
type: Sequelize.STRING,
require: true
},
lastname: {
type: Sequelize.STRING,
require: true
},
age: {
type: Sequelize.INTEGER,
require: true
},
phone: {
type: Sequelize.STRING,
require: true
},
location: {
type: Sequelize.STRING,
require: true
},
createdAt: {
type: Sequelize.DATE,
defaultValue: Sequelize.NOW
},
updatedAt: {
type: Sequelize.DATE,
defaultValue: Sequelize.NOW
}
});
return User;
};
MailModel :
module.exports = function (sequelize, Sequelize) {
var User = require('./User.js')(sequelize, Sequelize)
var Mail = sequelize.define('Mail', {
name: {
type: Sequelize.STRING,
require: true
}
});
Mail.belongsTo(User);
return Mail;
};
MailController :
var db = require('../services/db.js');
module.exports = {
create: function (req, res, next) {
var Mailm = db.MailModel;
var name = req.body;
try {
db.sequelize.sync().then(function () {
Mailm.create(name).then(function (found) {
return res.json({
success: true,
message: found.get({
plain: true
})
});
})
});
} catch (ex) {
res.json({
success: false,
exception: ex
});
return;
}
},
query: function (req, res, next) {
var Mailm = db.MailModel;
var Userm = db.UserModel;
var name = req.body;
var option = {};
option.where = name;
option.include = [{
model: Userm
}];
try {
Mailm.findAll(option).then(function (found) {
console.log(found);
return res.json({
success: true,
message: found
});
});
} catch (ex) {
res.json({
success: false,
exception: ex
});
return;
}
}
};
It is returning me the records of both User and Mail table in exactly the right way .
Output :
{
"success": true,
"message":[
{
"id": 2,
"name": "Mailb2",
"createdAt": "2015-07-30T07:32:51.807Z",
"updatedAt": "2015-07-30T07:32:51.807Z",
"UserUserId": 2,
"User":{
"userId": 2,
"firstname": "Prerna",
"lastname": "Jain",
"age": 20,
"phone": "9812123456",
"location": "Sirsa",
"createdAt": "2015-07-30T07:30:48.000Z",
"updatedAt": "2015-07-30T07:30:48.000Z"
}
}
]
}
But I want to disable createdAt and updatedAt fields of User table so that it does not give me these two fields in the output for User.
I have tried a lot as of how to do this but still in vain.Can anyone please help me out.
I bet this is coming late, add attribute/properties to your models called timestamps, it accepts a boolean as a value. For example:
module.exports = function (sequelize, Sequelize) {
var User = require('./User.js')(sequelize, Sequelize)
var Mail = sequelize.define('Mail', {
name: {
type: Sequelize.STRING,
require: true
}
},
{
// This does the magic
timestamps: false,
});
Mail.belongsTo(User);
return Mail;
};
Also, add it to the User model:
var User = sequelize.define('User', {
userId: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
firstname: {
type: Sequelize.STRING,
require: true
},
lastname: {
type: Sequelize.STRING,
require: true
},
age: {
type: Sequelize.INTEGER,
require: true
},
phone: {
type: Sequelize.STRING,
require: true
},
location: {
type: Sequelize.STRING,
require: true
},
createdAt: {
type: Sequelize.DATE,
defaultValue: Sequelize.NOW
},
updatedAt: {
type: Sequelize.DATE,
defaultValue: Sequelize.NOW
}
},
{
timestamps: false
});
return User;
};
You can use
Model.findAll({
attributes: { exclude: ['baz'] }
});
more examples with attributes - http://docs.sequelizejs.com/en/latest/docs/querying/#attributes