Returning parent attributes via sequelize - javascript

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
});

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;
};

Sequelize - Include based on specific attribute

I have a model defined as follows:
sequelize.define('game', {
id: {
type: type.INTEGER,
primaryKey: true,
autoIncrement: true,
},
leaderId: {
type: type.INTEGER,
allowNull: false,
references: {
model: 'users',
key: 'id',
},
},
playerId: {
type: type.INTEGER,
allowNull: false,
references: {
model: 'users',
key: 'id',
},
},
status: {
type: type.STRING,
defaultValue: 'running',
},
notes: {
type: type.TEXT,
},
});
I'm trying to use Sequelize to load all game object and include the User with the id equal to the playerId field.
The problem is I have two attributes (leaderId, playerId) which reference the User model so using include as follows does not work:
Game.findAll({
where: conditions,
include: [{ model: User }],
})
Is there a way to specify which attribute the include command should use?
const game = sequelize.define('game', {
id: {
type: type.INTEGER,
primaryKey: true,
autoIncrement: true,
},
leaderId: {
type: type.INTEGER,
allowNull: false,
references: {
model: 'users',
key: 'id',
},
},
playerId: {
type: type.INTEGER,
allowNull: false,
references: {
model: 'users',
key: 'id',
},
as:'player'
},
status: {
type: type.STRING,
defaultValue: 'running',
},
notes: {
type: type.TEXT,
},
});
game.associate = function (models) {
game.belongsTo(models.user, {
foreignKey: "playerId",
as: "player",
});
game.belongsTo(models.user, {
foreignKey: "leaderId",
as: "leader",
});
};
Game.findAll({
where: conditions,
include: ['player'],
})
Or
Game.findAll({
where: conditions,
include: [{model: User, as: 'player' }],
})
Or
Game.findAll({
where: conditions,
include: [{model: User, as: 'player', foreignKey: 'playerId' }],
})
https://github.com/nkhs/node-sequelize

Get data that contains all the ids of the array

I'm trying to make a filter songs, i have an array of genres ids that i retrieve from the client, i do this for get all the audios from one id:
Audio.findAll({
include: [{
model: db.Genres,
as: "genres",
where: {
id: {
[Op.and]: [1]
}
},
}]
})
But i need to get all audios that contains all the ids from array of genres/moods, also want filter audios by genres ids and moods ids, but i don't know how to make it, any idea? (One song can have many genres/moods)
Song Model
const Audio = sequelize.define('Audio', {
id: {
autoIncrement: true,
type: DataTypes.INTEGER(30),
allowNull: false,
primaryKey: true
},
name: {
type: DataTypes.STRING(255),
allowNull: false
},
})
Audio.associate = function(models) {
Audio.belongsToMany(models.Genres, {through: 'AudioGenres', foreignKey: 'id_audio', as: 'genres'})
Audio.belongsToMany(models.Moods, {through: 'AudioMoods', foreignKey: 'id_audio', as: 'moods'})
}
AudioGenreModel
const AudioGenres = sequelize.define('AudioGenres', {
id_audio: {
type: DataTypes.INTEGER(11),
allowNull: false,
primaryKey: true,
references: {
model: 'Audio',
key: 'id'
}
},
id_genre: {
type: DataTypes.INTEGER(11),
allowNull: false,
primaryKey: true,
references: {
model: 'Genres',
key: 'id'
}
})
AudioGenres.associate = function(models) {
AudioGenres.belongsTo(models.Audio, {foreignKey: 'id_audio'})
AudioGenres.belongsTo(models.Genres, {foreignKey: 'id_genre'})
};
AudioMoodModel
const AudioMoods = sequelize.define('AudioMoods', {
id_audio: {
type: DataTypes.INTEGER(11),
allowNull: false,
primaryKey: true,
references: {
model: 'Audio',
key: 'id'
}
},
id_mood: {
type: DataTypes.INTEGER(11),
allowNull: false,
primaryKey: true,
references: {
model: 'Mods',
key: 'id'
}
})
AudioMoods.associate = function(models) {
AudioMoods.belongsTo(models.Audio, {foreignKey: 'id_audio'})
AudioMoods.belongsTo(models.Mods, {foreignKey: 'id_mood'})
};
Moods and Genres Model
const Moods = sequelize.define('Moods', {
name: {
type: DataTypes.STRING(255),
allowNull: false
},
})
Moods.associate = function(models) {
Moods.belongsToMany(models.Audio, {through: 'AudioMoods', foreignKey: 'id_mood', as: 'audios'})
}
const Genres = sequelize.define('Genres', {
name: {
type: DataTypes.STRING(255),
allowNull: false
},
})
Genres.associate = function(models) {
Genres.belongsToMany(models.Audio, {through: 'AudioGenres', foreignKey: 'id_genre', as: 'audios'})
}
I suppose you should add all conditions in AND operator in both include options like this:
Audio.findAll({
include: [{
model: db.Genres,
as: "genres",
where: {
[Op.and]: [{ id: 1}, { id: 3},{ id: 2}]
},
}, {
model: db.Moods,
as: "moods",
where: {
[Op.and]: [{ id: 4}, { id: 5},{ id: 6}]
},
}]
})

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!

Sequelize.js - "is not associated to"

I have some issue with getting full data from db.
That are my models:
User
module.exports = function(sequelize, DataTypes) {
return sequelize.define('user', {
id: {
type: DataTypes.INTEGER(11),
allowNull: false,
primaryKey: true,
autoIncrement: true,
field: 'ID'
},
password: {
type: DataTypes.STRING(255),
allowNull: false,
field: 'password'
},
email: {
type: DataTypes.STRING(255),
allowNull: false,
unique: true,
field: 'email'
},
roleId: {
type: DataTypes.INTEGER(11),
allowNull: false,
references: {
model: 'role',
key: 'ID'
},
field: 'role_id'
}
}, {
timestamps: false,
tableName: 'user'
});
};
Role
module.exports = function(sequelize, DataTypes) {
return sequelize.define('role', {
id: {
type: DataTypes.INTEGER(11),
allowNull: false,
primaryKey: true,
autoIncrement: true,
field: 'ID'
},
name: {
type: DataTypes.STRING(255),
allowNull: false,
unique: true,
field: 'name'
},
description: {
type: DataTypes.STRING(255),
allowNull: false,
field: 'description'
},
permission: {
type: DataTypes.INTEGER(11),
allowNull: false,
field: 'permission'
}
}, {
timestamps: false,
tableName: 'role',
});};
I want to get object of one specific user including all role content.
Somethink like
{
id: 4,
password: 'xxx',
email: 'adsads#saas.com',
role: {
id: 2,
name: 'admin'
description: 'ipsum ssaffa',
permission: 30
}
}
So I'm using:
User.findOne( { where: { id: req.userId }, include: [ Role ] } ).then( user =>{...});
but I get in the result err.message: "role is not associated to user"
And the simple question - what's wrong ? :)
*to handle models I'm using sequelize-cli
You get this error because you didn't add associate between the models
base on your json I see that each user only has one role, so you can either use belongsTo in role model or hasOne in user model
Should be something like this:
User.js
module.exports = function(sequelize, DataTypes) {
var user = sequelize.define('user', {
id: {
type: DataTypes.INTEGER(11),
allowNull: false,
primaryKey: true,
autoIncrement: true,
field: 'ID'
},
password: {
type: DataTypes.STRING(255),
allowNull: false,
field: 'password'
},
email: {
type: DataTypes.STRING(255),
allowNull: false,
unique: true,
field: 'email'
},
roleId: {
type: DataTypes.INTEGER(11),
allowNull: false,
references: {
model: 'role',
key: 'ID'
},
field: 'role_id'
}
}, {
timestamps: false,
tableName: 'user'
});
user.associate = function(models) {
user.hasOne(models.role, {foreignKey: 'id',sourceKey: 'roleId'});
}
return user;
};
Role.js
module.exports = function(sequelize, DataTypes) {
var role = sequelize.define('role', {
id: {
type: DataTypes.INTEGER(11),
allowNull: false,
primaryKey: true,
autoIncrement: true,
field: 'ID'
},
name: {
type: DataTypes.STRING(255),
allowNull: false,
unique: true,
field: 'name'
},
description: {
type: DataTypes.STRING(255),
allowNull: false,
field: 'description'
},
permission: {
type: DataTypes.INTEGER(11),
allowNull: false,
field: 'permission'
}
}, {
timestamps: false,
tableName: 'role',
});
role.associate = function(models) {
user.belongsTo(models.role, {foreignKey: 'id'});
}
return role;
};
You have to declare associations between your Models. If using Sequelize CLI make sure the static method associate is being called. Example:
/models.index.js
const Category = require('./Category');
const Product = require('./Product');
const ProductTag = require('./ProductTag');
const Tag = require('./Tag');
Category.associate({Product});
Product.associate({Category,Tag});
Tag.associate({Product});
module.exports={Category,Product,ProductTag,Tag};
and then the association in Category.js
'use strict';
const {Model,DataTypes} = require('sequelize');
const sequelize = require('../config/connection.js');
class Category extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method.
*/
static associate({Product}) {
// define association here
console.log('Category associated with: Product');
this.hasMany(Product, {
foreignKey: 'category_id',
onDelete: 'CASCADE'
});
}
}
Category.init({
category_id: {type: DataTypes.INTEGER, autoIncrement: true, allowNull: false, primaryKey: true},
category_name: {type: DataTypes.STRING, allowNull: false}
}, {
sequelize,
timestamps: false,
freezeTableName: true,
underscored: true,
modelName: "Category",
});
module.exports = Category;

Categories