I'm new to Node and Sequelize, and i'm building a basic eCommerce.
These are my models:
const Product = sequelize.define('product', {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
allowNull: false,
primaryKey: true,
},
name: {
type: DataTypes.STRING,
allowNull: false,
},
});
const Sale = sequelize.define('sale', {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
allowNull: false,
primaryKey: true,
},
isDelivered: {
type: DataTypes.BOOLEAN,
defaultValue: false,
allowNull: false,
},
});
And these the relations:
Sale.belongsToMany(Product, { through: SaleItem });
Product.belongsToMany(Sale, { through: SaleItem });
The problem starts when I try to get the "SaleItems"
SaleItem.findAll({
include: [Product],
})
.then(response => {
console.log(response);
})
I get the response:
SequelizeEagerLoadingError: product is not associated to saleItem!
It's strange, couse I'm doing the same on a one-to-many relation, and works perfectly.
Thanks for your time :)
You have associations between Product and Sale but not between them and SaleItem.
Your existing associations let you execute only queries like this:
Sale.findAll({
include: [Product],
})
.then(response => {
console.log(response);
})
Product.findAll({
include: [Sale],
})
.then(response => {
console.log(response);
})
You need to add associations for SaleItem like this:
SaleItem.belongsTo(Product);
SaleItem.belongsTo(Sale);
in order to get SaleItem records along with associated model instances.
Related
I have three tables companies, subscriptions and companySubscription. As name defined company can canbuy/have plan or one subscription belongs to many companies.
So in model/schema I have defined as follows:
companies.js
const sequelize = require("../utils/database");
const bcrypt = require("bcrypt");
const { DataTypes, Model } = require("sequelize");
const subscription = require("./subscriptions");
const CompanySubscription = require("./companySubscription");
class companies extends Model {}
companies.init(
{
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
allowNull: false,
},
name: {
type: DataTypes.STRING,
allowNull: false,
},
email: {
type: DataTypes.STRING,
allowNull: false,
},
contactNo: {
type: DataTypes.STRING,
allowNull: true,
},
companySize: {
type: DataTypes.INTEGER,
allowNull: true,
},
},
{ sequelize, modelName: "companies" }
);
subscription.belongsToMany(companies, { through: CompanySubscription });
module.exports = companies;
subscription.js
const sequelize = require("../utils/database");
const { DataTypes, Model } = require("sequelize");
class subscription extends Model {}
subscription.init(
{
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
allowNull: false,
},
subscriptionPlanType: {
type: DataTypes.ENUM,
values: ["Yearly", "Monthly"],
allowNull: false,
},
name: {
type: DataTypes.STRING,
allowNull: false,
},
memberCount: {
type: DataTypes.INTEGER,
allowNull: false,
},
amount: {
type: DataTypes.FLOAT,
allowNull: false,
},
},
{ sequelize, modelName: "subscription" }
);
module.exports = subscription;
companySubscription.js
const sequelize = require("../utils/database");
const companies = require("./companies");
const subscription = require("./subscriptions");
const { DataTypes, Model } = require("sequelize");
class CompanySubscription extends Model {}
CompanySubscription.init(
{
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
allowNull: false,
},
status: {
type: DataTypes.ENUM,
values: ["active", "inactive"],
},
subscriptionType: {
type: DataTypes.ENUM,
values: ["Yearly", "Monthly"],
},
subscriptionPlanStartDate: {
type: DataTypes.DATE,
},
subscriptionPlanEndDate: {
type: DataTypes.DATE,
},
paidStatus: {
type: DataTypes.ENUM,
values: ["paid", "unpaid"],
},
paidDate: {
type: DataTypes.DATE,
},
},
{ sequelize, modelName: "CompanySubscription" }
);
module.exports = CompanySubscription;
In controller file I am able to manage to insert the data. Below is the code:
const addBIlling = async (req, res) => {
const foundSubcscription = await subscription.create({
subscriptionPlanType: "Monthly",
name: "s1",
memberCount: 15,
amount: 50.55,
});
const foundCompany = await companies.create({
name: "company1",
email: "company1#gmail.com",
contactNo: "87964644",
companySize: 20,
});
const insertedData = await foundSubcscription.addCompany(foundCompany, {
through: {
status: "active",
paidStatus: "paid",
subscriptionType: "Monthly",
subscriptionPlanEndDate: moment().add(1, "months"),
paidDate: moment().add(1, "months"),
},
});
console.log("inserted data ", insertedData);
res.json({ data: insertedData });
};
Now I want to fetch the records from db as which company has bought which subscription plan!
i.e. company name, subscription plan and its active and paid status and plan's expiry date.
I tried below code:
const billingList = async (req, res) => {
const billingData = await CompanySubscription.findAll({
include: [{ model: companies }],
});
console.log("billing data ", billingData);
};
Above code is throwing error "companies is not associated to CompanySubscription!".
Where have I made a mistake?
Don't try to import models to each other's modules directly. Define model registration functions in each model module and use them all to register models in one place/module and for associations you can define associate function inside each registration function and call them after ALL your models are already registered. That way you won't have cyclic dependencies and all associations will be correct.
See my answer here to get an idea how to do it.
I'm new to using Sequelize and for that I strictly follow the documentation here. It is written that we must use hasOne(), hasMany(), belongsTo() in order to add automatically the foreign keys. In my situation: I have a Category and a FAQ model, defined so:
Category.js
const { Sequelize } = require('sequelize');
const sequelize = require('../database/connection');
const Category = sequelize.define('Category', {
id: {
type: Sequelize.DataTypes.INTEGER,
allowNull: false,
autoIncrement: true,
primaryKey: true
},
categoryShop_id: {
type: Sequelize.DataTypes.UUID,
allowNull: true
},
name: {
type: Sequelize.DataTypes.STRING,
allowNull: false
},
active: {
type: Sequelize.DataTypes.BOOLEAN,
allowNull: false,
defaultValue: true
},
parent_id: {
type: Sequelize.DataTypes.INTEGER,
allowNull: true
}
});
Category.associate = (models) => {
Category.hasMany(models.faqs, {
onDelete:'CASCADE',
onUpdate:'CASCADE'
});
};
module.exports = Category;
Faq.js
const { Sequelize } = require('sequelize');
const sequelize = require('../database/connection');
const Faq = sequelize.define('Faq', {
id: {
type: Sequelize.DataTypes.INTEGER,
allowNull: false,
autoIncrement: true,
primaryKey: true
},
question: {
type: Sequelize.DataTypes.TEXT,
allowNull: false
},
answer: {
type: Sequelize.DataTypes.TEXT,
allowNull: false
},
product_id: {
type: Sequelize.DataTypes.STRING,
allowNull: true
},
active: {
type: Sequelize.DataTypes.BOOLEAN,
allowNull: false,
defaultValue: true
}
});
Faq.associate = (models) => {
Faq.belongsTo(models.categories, {
foreignKey: {
name: 'category_id',
allowNull: true
}
});
};
module.exports = Faq;
The migrations run without any errors, but I don't see the added columns to the table. What is the reason for this?
Your confusing model and migration code. Your migrations are ok, but lack the right foreign key columns for your associations.
Your association code looks ok but it belongs in your model.
Look at https://sequelizeui.app/ for some example code.
I am trying to associate two tables in Sequelize but I am getting the SequelizeEagerLoadingError that one table is not associated to another despite trying all the available fixes on this platform.
I have two tables, User and Item.
User (user.js)
const User = dbconnection.sequelize.define('users', {
id: { type: Sequelize.INTEGER, autoIncrement: true, primaryKey: true},
name: {
type: Sequelize.STRING(80),
allowNull: false
},
email: {
type: Sequelize.STRING(120),
allowNull: false,
unique: true
},
dob: {
type: Sequelize.DATEONLY,
allowNull: false
},
password: {
type: Sequelize.STRING(256),
allowNull: false
}
});
User.associate = models => {
User.hasMany(models.Item, { as: 'items',foreignKey: 'user_id' })
}
dbconnection.sequelize.sync({ force: false })
.then(() => {
//console.log('Table created!')
});
module.exports = {
User
};
Item (item.js)
const Item = dbconnection.sequelize.define('items', {
id: { type: Sequelize.INTEGER, unique: true, autoIncrement: true, primaryKey: true},
item: {
type: Sequelize.STRING(80),
allowNull: true
},
item_type: {
type: Sequelize.STRING(10),
allowNull: false
},
comment: {
type: Sequelize.STRING(1000),
allowNull: true
},
user_id: {
type: Sequelize.INTEGER,
allowNull: false,
references: { model: 'users', key: 'id' }
},
});
Item.associate = models => {
Item.belongsTo(models.User, { as: 'users',foreignKey: 'user_id' })
}
dbconnection.sequelize.sync({ force: false })
.then(() => {
// console.log('Table created!')
})
});
module.exports = {
Item
};
User hasMany(Item) while Item belongsTo(User) as shown above.
However, when I make a query to the Item table (as below),
const usersdb = require('./userdb')
const itemsdb = require('./itemdb')
class ItemsController {
static async getAllItems(req, res, next) {
try{
let allitems = await itemsdb.Item.findAll({
include: [{
model: usersdb.User
}]
})
return {items: allitems, status: true}
}
catch (e) {
return {items: e, status: false}
}
}
}
module.exports = ItemsController;
I get the SequelizeEagerLoadingError that "users is not associated to items!"
I have tried all the available fixes including this and this among others but to no success.
I have finally found a workaround. First, I dropped the tables and discarded the model definitions. Second, I generated migrations and models using the sequelize model:create --name ModelName --attributes columnName:columnType command. I then used the generated models to associate the two tables just as I had done earlier. Lastly, I ran the sequelize db:migrate command to create the tables and on running the query, it worked!
Earlier, I was creating the models manually. I was also creating the tables using the sequelize.sync({force: false/true}) command after loading the models.
User Model (user.js)
'use strict';
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define('User', {
email: {
type: DataTypes(120),
allowNull: false,
unique: true
},
dob: {
type: DataTypes.DATEONLY,
allowNull: false
},
password: {
type: DataTypes.STRING(256),
allowNull: false
}
}, {});
User.associate = function(models) {
User.hasMany(models.Item, {as: 'Item', foreignKey: 'user_id'})
};
return User;
};
Item model (item.js)
'use strict';
module.exports = (sequelize, DataTypes) => {
const Item = sequelize.define('Item', {
item: {
type: DataTypes.STRING(80),
allowNull: true
},
item_type: {
type: DataTypes.STRING(10),
allowNull: false
},
comment: {
type: DataTypes.STRING(1000),
allowNull: true
},
user_id: {
type: DataTypes.INTEGER,
allowNull: false,
references: { model: 'User', key: 'id' }
}
}, {});
Item.associate = function(models) {
Item.belongsTo(models.User, { as: 'User',foreignKey: 'user_id' })
};
return Item;
};
Query (queryitem.js)
const Item = require('../models').Item
const User = require('../models').User
class ItemsController {
static async getAllItems() {
try{
let allitems = await Item.findAll({
include: [{
model: User,
as: 'User'
}]
})
return {items: allitems, status: true}
}
catch (e) {
return {items: e, status: false}
}
}
}
module.exports = ItemsController;
I am relatively new to NodeJS and SequelizeJS and am facing a hasOne issue with a query I am building and I'd like to know your thoughts about this issue to find out where I gone wrong and the correct way to implement this query.
Association Here
The models where generated using sequelize-auto (pg-hstore).
Bloco Model:
module.exports = function(sequelize, DataTypes) {
return sequelize.define('bloco_condominio', {
id_bloco: {
type: DataTypes.INTEGER,
allowNull: false,
autoIncrement: true,
primaryKey: true
},
id_condominio: {
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: 'condominio',
key: 'id_condominio'
}
},
nm_bloco: {
type: DataTypes.STRING,
allowNull: true
},
ic_status: {
type: DataTypes.STRING,
allowNull: false,
defaultValue: "A"
}
}, {
tableName: 'bloco_condominio'
});
};
Apartamento Model:
module.exports = function(sequelize, DataTypes) {
return sequelize.define('apartamento', {
id_apartamento: {
type: DataTypes.INTEGER,
allowNull: false,
autoIncrement: true,
primaryKey: true
},
id_condominio: {
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: 'condominio',
key: 'id_condominio'
}
},
nu_apto: {
type: DataTypes.STRING,
allowNull: true
},
id_bloco: {
type: DataTypes.INTEGER,
allowNull: true,
references: {
model: 'bloco_condominio',
key: 'id_bloco'
}
},
ic_status: {
type: DataTypes.STRING,
allowNull: false,
defaultValue: "A"
},
dt_incl: {
type: DataTypes.TIME,
allowNull: false,
defaultValue: sequelize.fn('now')
},
dt_ult_alt: {
type: DataTypes.TIME,
allowNull: false,
defaultValue: sequelize.fn('now')
}
}, {
tableName: 'apartamento'
});
};
Apartamento Service:
"use strict";
var model = require('../models');
var Utils = require('../utils/utils');
var service = {};
var Apartamento = model.apartamento;
var Bloco = model.bloco_condominio;
var Morador = model.morador;
var Pessoa = model.pessoa;
//Incluir relação OneToMany
Apartamento.hasMany(Morador, { as: "Moradores", foreignKey: 'id_apartamento' });
Morador.belongsTo(Apartamento, { foreignKey: 'id_apartamento' });
Morador.hasMany(Pessoa, { as: "Pessoa", foreignKey: 'id_pessoa' });
Pessoa.belongsTo(Morador, { foreignKey: 'id_pessoa' });
Bloco.hasMany(Apartamento, { as: "Bloco", foreignKey: 'id_bloco' });
Apartamento.hasMany(Bloco, { foreignKey: 'id_bloco' });
service.getApartamentoById = function(idApartamento) {
return Apartamento.findById(idApartamento, {
include: [
{ model: Morador, as: 'Moradores', include: [
{ model: Pessoa, as: 'Pessoa'}
]},
{ model: Bloco, as: 'Bloco' }
]
})
.then(function(data) {
return data;
})
.catch(function(err) {
throw 'Erro ao consultar apartamento por ID: ' + err.message + ' - Request: '+JSON.stringify(idApartamento);
});
};
I can perfectly retrieve the other hasMany associations, but still hasn't found a way to do so in the reverse way.
Do you guys have any idea of how I should approach this issue in the correct manner?
Thanks in advance for your help!
Best regards,
Enrico Bergamo
To make it simpler for me (only knowing English), I've grabbed the following from Google translate:
Pessoa: Person
Morador: Dweller
Bloco: Block
Apartmento: Apartment
So, Dweller can have many People, an Apartment can have many Dwellers and a Block can have many Apartments.
Your definition on the other models indicates they're all 1:m, so I followed that assumption for Apartments and Blocks.
With that in mind, the following should work.
Bloco.hasMany(Apartamento, { as: "Apartmento", foreignKey: 'id_bloco' });
Apartamento.belongsTo(Bloco, { foreignKey: 'id_bloco' });
Note: I've changed the as: "Bloco" to as: "Apartmento" and the second hasMany to belongsTo. This might be where your issues were coming from.
Edit: The method to access the Apartments that belong to a Block is:
bloco.getApartmento(options)
I have this working with this promise chain:
Bloco.create()
.then(block => {
return Promise.all([
block,
Apartamento.bulkCreate([{
id_bloco: block.id_bloco
}, {
id_bloco: block.id_bloco
}, {
id_bloco: block.id_bloco
}, {
id_bloco: block.id_bloco
}, {}])
])
})
.spread((bloco, apartment) => {
return bloco.getApartamento()
})
.then(apartments => {
console.log(apartments.length); --> Logs 4 which matches the bulk create.
})
If I've misinterpreted, and it should be an n:m relationship (Apartments/Blocks), then you should use belongsToMany on each model and identify the through option.
Bloco.belongsToMany(Apartamento, {
as: "Apartmentos",
foreignKey: 'id_bloco',
through: "BlocoAparmento"
});
Apartamento.belongsToMany(Bloco, {
as: "Blocos",
foreignKey: 'id_apartmento',
through: "BlocoAparmento"
});
This will create an n:m joining table called "BlockApartmento". If you define that model, and use the model instead of the string, you'll have complete control over the models settings.
This will give you the Bloco.getApartmentos( methods as well as opposite (Apartmento.getBlocos() along with setAssociation, addAssoc... etc
http://docs.sequelizejs.com/manual/tutorial/associations.html#belongs-to-many-associations
I have 4 tables/models that shares ids as primary keys, but for the example I will just show 2
User: {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
allowNull: false
}
Person: {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
allowNull: false
},
name: {
type: Sequelize.STRING
}
I am using shared ids, so the person primary key is the primary key of User, it doesn't have autoimcrement. So is there a way to make a join between those 2 tables? I've tried to do something like:
getAll(){
return User.getAll({
include: [{
model:Person,
}]
})
}
But since there is no relation it doesn't work. What should I do?
Thanks
You cannot use include without a relationship. You could use a raw query to retrieve the information like:
var queryString = 'select * from User u join Person p on u.id = p.id where u.id = :sharedId';
models.sequelize
.query(queryString, {
replacements: { shareId: id},
type: models.sequelize.QueryTypes.SELECT
})
.then(function(result) {
res.json(attendance);
});
But then you would have to implement validations to make sure tables are filled up correclty.
However I would encourage you to implement relationships between your tables as It would be more maintainable. Something like this:
var User = sequelize.define("User", {
id: { type: DataTypes.INTEGER, primaryKey: true},
name: { type: DataTypes.STRING, name: 'name' },
...
}, {
classMethods: {
associate: function(models) {
User.hasOne(models.Person);
}
}
});
var Person = sequelize.define("Person", {
id: { type: DataTypes.INTEGER, primaryKey: true},
name: { type: DataTypes.STRING, name: 'name' },
...
}, {
classMethods: {
associate: function(models) {
Person.belogsTo(models.User);
}
}
});