Sequelize: "Model A is not associated to Model B" - javascript

I have issue: "is not associated to". I want to have one to many association. Here is my code
Index.js
fs.readdirSync(__dirname)
.filter(file => {
return file.indexOf('.') !== 0 && file !== basename && file.slice(-3) === '.js';
})
.forEach(file => {
const model = require(path.join(__dirname, file))(sequelize, Sequelize);
db[model.name] = model;
});
Object.keys(db).forEach(modelName => {
if (db[modelName].associate) {
db[modelName].associate(db);
}
});
db.Sequelize = Sequelize;
db.sequelize = sequelize;
module.exports = db;
Here is course.js
module.exports = (sequelize, Sequelize) => {
const Course = sequelize.define(
'course',
{
idCourse: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
field: 'idcourse',
},
nameOfCourse: {
type: Sequelize.STRING,
field: 'nameofcourse',
},
idLevel: {
type: Sequelize.INTEGER,
field: 'idlevel',
},
idTypeOfCourse: {
type: Sequelize.INTEGER,
field: 'idtypeofcourse',
},
startDate: {
type: Sequelize.DATE,
field: 'startdate',
},
endDate: {
type: Sequelize.DATE,
field: 'enddate',
},
fee: {
type: Sequelize.BIGINT,
field: 'fee',
},
isDeleted: {
type: Sequelize.BOOLEAN,
field: 'isdeleted',
},
},
{
freezeTableName: true,
timestamps: false,
createdAt: false,
updatedAt: false,
}
);
Course.associate = function (models) {
models.course.belongsToMany(models.bill, {
through: models.billinfo,
as: 'bill',
foreignKey: 'idCourse',
});
models.course.belongsTo(models.typeofcourse, {
foreignKey: 'idTypeOfCourse',
sourceKey: 'idCourse',
as: 'typeofcourse',
});
};
return Course;
};
Here is TypeOfCourse.js
module.exports = (sequelize, Sequelize) => {
const TypeOfCourse = sequelize.define(
'typeofcourse',
{
idTypeOfCourse: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
field: 'idtypeofcourse',
},
nameOfType: {
type: Sequelize.STRING,
field: 'nameoftype',
},
language: {
type: Sequelize.STRING,
field: 'language',
},
tags: {
type: Sequelize.ARRAY(Sequelize.TEXT),
field: 'tags',
},
isDeleted: {
type: Sequelize.BOOLEAN,
field: 'isdeleted',
},
},
{
freezeTableName: true,
timestamps: false,
createdAt: false,
updatedAt: false,
}
);
TypeOfCourse.associate = function (models) {
models.typeofcourse.hasMany(models.course, {
foreignKey: 'idTypeOfCourse',
as: 'course',
});
};
return TypeOfCourse;
};
In course.controller.js, I define function findAll Course and want to display info about typeOfCourse
exports.findAll = (req, res) => {
Course.findAll({
include: [
{
model: TypeOfCourse,
as: 'typeofcourse',
through: { attributes: [] },
},
],
})
.then(data => {
res.send(data);
})
.catch(err => {
res.status(500).send({
message: err.message || 'Some error occurred while retrieving course.',
});
});
};
But when i send request to get course. This message responed:
{
"message": "typeofcourse is not associated to course!"
}
Where I wrong and how to fix this error. Thank you so much.

You can use the hasMany association, it will Create an N:M association with a join table
Course.hasMany(TypeOfCourse, {
foreignKey: { name: 'idCourse' },
targetKey: 'idCourse',
constraints: false,
as: 'typeOfCourses',
}),
```

Related

Sequelize M:N Association - "TypeError: Members.belongsToMany is not a function"

I have two tables in a MySQL schema, 'members' and 'events', which share a many-to-many relationship. I am attempting to model that relationship using Sequelize via a junction table containing a member_id and an event_id (which correspond to columns in the Members and Events tables respectively) and an event_date - the date when an event is attended by many members.
I am following the guidance in the Sequelize 'Advanced Associations' section (link), but am getting an error when my node.js server attempts to start, as follows:
Members.belongsToMany(models.Events, { through: 'member_events' })
^
TypeError: Members.belongsToMany is not a function
I'm really stuggling to understand what specifically this means and how I can address the issue. The following is my code for the three models in question:
memberEvents.js
module.exports = (sequelize, DataTypes) => {
const Members = require('../models/members')
const Events = require('../models/events')
const MemberEvents = sequelize.define(
"MemberEvents",
{
member_id: {
type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true,
},
event_id: {
type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true,
},
event_date: {
type: DataTypes.DATEONLY,
allowNull: false,
primaryKey: true,
},
},
{ tableName: "member_events" }
);
Members.belongsToMany(Events, { through: 'member_events' })
Events.belongsToMany(Members, { through: 'member_events' })
return MemberEvents;
};
Members.js
module.exports = (sequelize, DataTypes) => {
//Below creates the member table in the schema
const Members = sequelize.define(
"Members",
{
member_id: {
type: DataTypes.INTEGER,
autoIncrement: true,
allowNull: false,
primaryKey: true,
},
forename: {
type: DataTypes.STRING(35),
allowNull: false,
},
surname: {
type: DataTypes.STRING(35),
allowNull: false,
},
date_of_birth: {
type: DataTypes.DATEONLY,
allowNull: false,
},
address_1: {
type: DataTypes.STRING(35),
allowNull: false,
},
address_2: {
type: DataTypes.STRING(35),
},
address_3: {
type: DataTypes.STRING(35),
},
address_4: {
type: DataTypes.STRING(35),
},
address_5: {
type: DataTypes.STRING(35),
},
postcode: {
type: DataTypes.STRING(12),
allowNull: false,
},
directions: {
type: DataTypes.TEXT("long"),
},
mobile_phone: {
type: DataTypes.STRING(12),
},
email_address: {
type: DataTypes.STRING(255),
},
key_safe_code: {
type: DataTypes.STRING(8),
},
next_of_kin_name: {
type: DataTypes.STRING(70),
},
next_of_kin_phone: {
type: DataTypes.STRING(12),
},
next_of_kin_local: {
type: DataTypes.STRING(33),
},
next_of_kin_relationship: {
type: DataTypes.STRING(40),
},
doctor_name: {
type: DataTypes.STRING(35),
},
initial_medical_conditions: {
type: DataTypes.TEXT("long"),
},
deceased: {
type: DataTypes.DATEONLY,
},
normally_escorted: {
type: DataTypes.STRING(3),
},
blue_badge_holder: {
type: DataTypes.STRING(3),
},
medical_equipment: {
type: DataTypes.STRING,
},
},
{ tableName: "Member" }
);
return Members;
};
Events.js
module.exports = (sequelize, DataTypes) => {
//Below creates the event table in the schema
const Events = sequelize.define(
"Events",
{
event_id: {
type: DataTypes.INTEGER,
autoIncrement: true,
allowNull: false,
primaryKey: true,
},
event_name: {
type: DataTypes.STRING(70),
allowNull: false,
},
staff_id: {
type: DataTypes.INTEGER,
allowNull: false,
},
},
{ tableName: "Events" }
);
return Events;
};
When separating your Sequelize models into separate imports, use the associate function to access the models to make these associations. You also don't need to import the other models into each other.
Something along these lines should work:
TableA.js
module.exports = (sequelize, DataTypes) => {
const TableA = sequelize.define('table_a', {
foobar: DataTypes.STRING,
}, {});
TableA.associate = function(models) {
TableA.belongsTo(models.TableB, { through: 'table_c' });
};
return TableA;
};
TableB.js
module.exports = (sequelize, DataTypes) => {
const TableB = sequelize.define('table_b', {
fazbaz: DataTypes.STRING,
}, {});
TableB.associate = function(models) {
TableB.belongsTo(models.TableA, { through: 'table_c' });
};
return TableB;
};

Sequelise : Many To Many table(CROSS TABLE) associated to other table

This is my Diagram DATABASE : https://i.stack.imgur.com/CGAwh.png
I made models of my databases with SEQUELIZE like that :
MODEL : Level
module.exports = (sequelize, DataTypes) => {
const Level = sequelize.define(
'Level',
{
level_id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
},
label: {
type: DataTypes.STRING,
allowNull: false,
unique: {
args: true,
msg: 'Level:Label already exist!',
},
validate: {
notEmpty: { msg: `Level:Label cannot be empty!` },
notNull: { msg: `Level:Label cannot be NULL!` },
},
},
ref: {
type: DataTypes.STRING,
allowNull: true,
},
description: {
type: DataTypes.TEXT,
allowNull: true,
},
},
{
tableName: 'levels',
timestamps: false,
}
);
Level.associate = (models) => {
Level.belongsToMany(models.Test, {
through: models.testHasLevel,
foreignKey: 'level_id',
otherKey: 'test_id',
timestamps: false,
});
};
return Level;
};
Model : TEST :
module.exports = (sequelize, DataTypes) => {
const Test = sequelize.define(
'Test',
{
test_id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
},
label: {
type: DataTypes.STRING,
allowNull: false,
validate: {
notEmpty: { msg: `Test:label cannot be empty!` },
notNull: { msg: `Test:label cannot be NULL!` },
},
},
isInternal: {
type: DataTypes.BOOLEAN,
defaultValue: false,
allowNull: false,
validate: {
notEmpty: { msg: `Test:isInternal cannot be empty!` },
notNull: { msg: `Test:isInternal cannot be NULL!` },
},
},
parent_id: {
type: DataTypes.INTEGER,
defaultValue: null,
allowNull: true,
},
},
{
tableName: 'tests',
timestamps: false,
}
);
Test.associate = (models) => {
Test.belongsToMany(models.Level, {
through: models.testHasLevel,
foreignKey: 'test_id',
otherKey: 'level_id',
timestamps: false,
});
Test.hasMany(models.Test, { foreignKey: 'parent_id', as: 'children' });
};
return Test;
};
MODEL : TEST HAS MODEL
module.exports = (sequelize, DataTypes) => {
const testHasLevel = sequelize.define(
'testHasLevel',
{},
{
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
},
tableName: 'test_has_level',
timestamps: false,
}
);
testHasLevel.associate = (models) => {
testHasLevel.belongsTo(models.Test, {
foreignKey: 'test_id',
targetKey: 'test_id',
});
testHasLevel.belongsTo(models.Level, {
foreignKey: 'level_id',
targetKey: 'level_id',
});
};
return testHasLevel;
};
I made also SESSION MODEL :
module.exports = (sequelize, DataTypes) => {
const Session = sequelize.define(
'Session',
{
session_id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
},
institut_id: {
type: DataTypes.INTEGER,
},
start: {
type: DataTypes.DATE,
},
end: {
type: DataTypes.DATE,
},
test_id: {
type: DataTypes.INTEGER,
},
level_id: {
type: DataTypes.INTEGER,
},
limitDateSubscribe: {
type: DataTypes.DATE,
},
placeAvailable: {
type: DataTypes.INTEGER,
},
},
{
tableName: 'sessions',
timestamps: false,
}
);
Session.associate = (models) => {
Session.hasMany(models.sessionHasUser, { foreignKey: 'session_id' });
};
return Session;
};
But i have no idea how to "BIND" SESSION with TEST_HAS_LEVEL with Sequelize ....
What should i change ? cause i know "composite key" are not allowed with the last version of sequelize.
In other term :
How associate properly a cross table with a one to many relationship to an other table ?
Model: Level
module.exports = (sequelize, DataTypes) => {
const Level = sequelize.define(
"Level",
{
level_id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
},
label: {
type: DataTypes.STRING,
allowNull: false,
unique: {
args: true,
msg: "Level:Label already exist!",
},
validate: {
notEmpty: { msg: `Level:Label cannot be empty!` },
notNull: { msg: `Level:Label cannot be NULL!` },
},
},
ref: {
type: DataTypes.STRING,
allowNull: true,
},
description: {
type: DataTypes.TEXT,
allowNull: true,
},
},
{
tableName: "levels",
timestamps: false,
}
);
Level.associate = (models) => {
Level.hasMany(models.testHasLevel, {
foreignKey: "level_level_id",
as: "levels",
});
};
return Level;
};
Model: Test
module.exports = (sequelize, DataTypes) => {
const Test = sequelize.define(
"Test",
{
test_id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
},
label: {
type: DataTypes.STRING,
allowNull: false,
validate: {
notEmpty: { msg: `Test:label cannot be empty!` },
notNull: { msg: `Test:label cannot be NULL!` },
},
},
isInternal: {
type: DataTypes.BOOLEAN,
defaultValue: false,
allowNull: false,
validate: {
notEmpty: { msg: `Test:isInternal cannot be empty!` },
notNull: { msg: `Test:isInternal cannot be NULL!` },
},
},
parent_id: {
type: DataTypes.INTEGER,
defaultValue: null,
allowNull: true,
},
},
{
tableName: "tests",
timestamps: false,
}
);
Test.associate = (models) => {
Test.hasMany(models.testHasLevel, {
foreignKey: "test_test_id",
as: "tests",
});
Test.hasMany(models.Test, { foreignKey: "parent_id", as: "children" });
};
return Test;
};
Model: Test has level
module.exports = (sequelize, DataTypes) => {
const testHasLevel = sequelize.define(
"testHasLevel",
{
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
},
test_test_id: {
type: DataTypes.INTEGER,
},
level_level_id: {
type: DataTypes.INTEGER,
},
},
{
tableName: "test_has_level",
timestamps: false,
}
);
testHasLevel.associate = (models) => {
testHasLevel.belongsTo(models.Test, {
foreignKey: "test_test_id",
as: "tests",
});
testHasLevel.belongsTo(models.Level, {
foreignKey: "level_level_id",
as: "levels",
});
testHasLevel.hasMany(models.Session, {
foreignKey: "test_has_level_id",
as: "test_has_level",
});
};
return testHasLevel;
};
Model: Session
module.exports = (sequelize, DataTypes) => {
const Session = sequelize.define(
"Session",
{
session_id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
},
institut_id: {
type: DataTypes.INTEGER,
},
start: {
type: DataTypes.DATE,
},
end: {
type: DataTypes.DATE,
},
test_has_level_id: {
type: DataTypes.INTEGER,
},
limitDateSubscribe: {
type: DataTypes.DATE,
},
placeAvailable: {
type: DataTypes.INTEGER,
},
},
{
tableName: "sessions",
timestamps: false,
}
);
Session.associate = (models) => {
Session.belongsTo(models.testHasLevel, {
foreignKey: "test_has_level_id",
as: "test_has_level",
});
};
return Session;
};

GraphQL with Sequelize returns null on a hasOne relation

I defined Cars, Brands, and Images. Cars return all images but it returns Brand as null. (I don't show the definition of Images because it is working just fine, unlike Brand)
Each car has only one brand and multiple images so I defined as follows:
Cars.associate = models => {
Cars.hasOne(models.brands, {
foreignKey: {
name: 'id',
allowNull: false
},
sourceKey: 'brand_id',
required : true
}),
Cars.hasMany(models.car_images, {
foreignKey: {
name: 'car_id',
allowNull: false
},
onDelete: "cascade"
})
};
Then I defined Brands and I associated to Cars:
CarBrands.associate = models => {
CarBrands.belongsTo(models.cars, {
sourceKey: 'id',
foreignKey: {
name: 'id',
}
});
The SQL is built fine, all the information is returned yet when I query the GraphQL brand returns always null:
SELECT `cars`.`id`, `cars`.`user_id`, `cars`.`title`, `cars`.`created_at`, `cars`.`updated_at`, `brand`.`id` AS `brand.id`, `brand`.`name` AS `brand.name`, `brand`.`status` AS `brand.status`, `car_images`.`id` AS `car_images.id`, `car_images`.`car_id` AS `car_images.car_id`, `car_images`.`image` AS `car_images.image`, `car_images`.`created_at` AS `car_images.created_at`, `car_images`.`updated_at` AS `car_images.updated_at` FROM `cars` AS `cars` LEFT OUTER JOIN `brands` AS `brand` ON `cars`.`brand_id` = `brand`.`id` LEFT OUTER JOIN `car_images` AS `car_images` ON `cars`.`id` = `car_images`.`car_id`;
When I query:
brand_name {
name
}
It returns:
"brand_name": null
The definitions are as follows:
Cars.js
export const typeDef = `
type Cars {
user_id: Int!,
title: String!,
brand_id: Int!,
brand_name: [CarBrands],
car_images: [CarImages]
}
`;
export const resolvers = {
Query: {
async getAllCars (root, args, { models }) {
return models.cars.findAll({
include: [{ all: true, nested: true }]
})
},
}
};
CarBrands.js
export const typeDef = `
type CarBrands {
id: Int!,
name: String,
status: Int
}
`;
cars.js
module.exports = function(sequelize, DataTypes) {
const Cars = sequelize.define('cars', {
'id': {
type: DataTypes.INTEGER(11),
allowNull: false,
primaryKey: true,
comment: "null",
autoIncrement: true
},
'user_id': {
type: DataTypes.INTEGER(11),
allowNull: true,
comment: "null"
}
}, {
updatedAt: 'updated_at',
createdAt: 'created_at',
tableName: 'cars',
underscored: true
});
Cars.associate = models => {
Cars.hasOne(models.brands, {
foreignKey: {
name: 'id',
allowNull: false
},
sourceKey: 'brand_id',
required : true
}),
Cars.hasMany(models.car_images, {
foreignKey: {
name: 'car_id',
allowNull: false
},
onDelete: "cascade"
})
};
return Cars;
};
brands.js
module.exports = function(sequelize, DataTypes) {
const CarBrands = sequelize.define('brands', {
'id': {
type: DataTypes.INTEGER(11),
allowNull: false,
primaryKey: true,
comment: "null",
autoIncrement: true
},
'name': {
type: DataTypes.STRING(255),
allowNull: true,
comment: "null"
},
'status': {
type: DataTypes.INTEGER(3),
allowNull: false,
defaultValue: '1',
comment: "null"
}
}, {
timestamps: false,
tableName: 'brands'
});
CarBrands.associate = models => {
CarBrands.belongsTo(models.cars, {
sourceKey: 'id',
foreignKey: {
name: 'id',
}
});
};
return CarBrands;
};
Any idea where is the problem?
Thanks!

Returning parent attributes via sequelize

I am working on a NodeJs application using sequelize as an ORM with exciting database , so I had to use sequelize model generator in order to generate the models for my application.
Here's an example of the generation output:
Category.js
module.exports = function(sequelize, DataTypes) {
return sequelize.define('category', {
id: {
type: DataTypes.STRING(128),
allowNull: false,
primaryKey: true,
field: 'id'
},
name: {
type: DataTypes.TEXT,
allowNull: true,
field: 'name'
}
}, {
tableName: 'category'
});
};
Product.js
module.exports = function(sequelize, DataTypes) {
return sequelize.define('product', {
id: {
type: DataTypes.STRING(128),
allowNull: false,
primaryKey: true,
field: 'id'
},
category: {
type: DataTypes.STRING(128),
allowNull: false,
references: {
model: 'category',
key: 'id'
},
field: 'category'
},
name: {
type: DataTypes.TEXT,
allowNull: true,
field: 'name'
}
}, {
tableName: 'product'
});
};
and then inside my controller I have this query :
models.product.findOne({
where: {
id: req.body.id
}
}).then(function (obj) {
//return the product data
console.log(product.category) //works
console.log(product.category.name) //return undefined
});
The question is how can I access to the parent table attribute via the same query findOne ? Is there something like or equivalent to product.category.id ?
if you have associated both the models... then try this
module.exports = function(sequelize, DataTypes) {
return sequelize.define('product', {
id: {
type: DataTypes.STRING(128),
allowNull: false,
primaryKey: true,
field: 'id'
},
category_id: {
type: DataTypes.STRING(128),
allowNull: false,
references: {
model: 'category',
key: 'id'
},
field: 'category'
},
name: {
type: DataTypes.TEXT,
allowNull: true,
field: 'name'
}
}, {
tableName: 'product'
});
}
models.product.findOne({
where: {
id: req.body.id
},
include: [{
model: category,
required: false
}]
}).then(function (obj) {
//return the product data
console.log(product.category) //works
console.log(product.category.name) //return undefined
});
Associate like this
product.hasMany(db.category, {
foreignKey: 'category_id',
onDelete: 'cascade'
});
category.belongsTo(db.product, {
foreignKey: 'category_id',
targetKey: 'id',
constraints: true
});

Query sequelize table without column 'id'

I have the following model
(Migration) transaction.js
'use strict';
module.exports = {
up: function(queryInterface, Sequelize) {
return queryInterface.createTable('transactions', {
date: {
type: Sequelize.DATEONLY
},
transNo: {
type: Sequelize.INTEGER,
allowNull: false,
autoIncrement: true,
primaryKey: true,
},
accNo: {
type: Sequelize.INTEGER
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: function(queryInterface, Sequelize) {
return queryInterface.dropTable('transactions');
}
};
(Model) transaction.js
'use strict';
const credit = require('./credit.js');
module.exports = function(sequelize, DataTypes) {
var transaction = sequelize.define('transaction', {
date: DataTypes.DATEONLY,
transNo: DataTypes.INTEGER,
accNo: DataTypes.INTEGER
}, {
classMethods: {
associate: function(models) {
// associations can be defined here
transaction.hasOne(models.credit, {foreignKey: 'transNo'});
}
}
});
return transaction;
};
(Model) credit.js
module.exports = function(sequelize, DataTypes) {
var credit = sequelize.define('credit', {
transNo: DataTypes.INTEGER,
creNo: DataTypes.INTEGER
}, {
classMethods: {
associate: function(models) {
// associations can be defined here
}
}
});
return credit;
};
(Migration) credit.js
` module.exports = {
up: function(queryInterface, Sequelize) {
return queryInterface.createTable('credits', {
transNo: {
type: Sequelize.INTEGER
},
creNo: {
type: Sequelize.INTEGER,
autoIncrement: true,
allowNull: false,
primaryKey: true
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: function(queryInterface, Sequelize) {
return queryInterface.dropTable('credits');
}
};`
it still queries with the id column
Executing (default): SELECT transaction.id, transaction.date, transaction.accNo,.........(and so on).
how to disable the id column from being queried?
You simply need to set another column as your primary key and the id column won't be generated by default.
module.exports = function(sequelize, DataTypes) {
var credit = sequelize.define('credit', {
transNo: DataTypes.INTEGER,
{
creNo: DataTypes.INTEGER,
primaryKey: true //define a primaryKey
}
}, {
classMethods: {
associate: function(models) {
// associations can be defined here
}
}
});
return credit;
};

Categories