Sequelize findOrCreate including associates - "Cannot read property 'field' of undefined" - javascript

Hello I'm using the findOrCreate method to insert records, but it seems that the method does not work on include associates. Getting error if the include associate exists in DB
Using the following inputs.
Cannot read property 'field' of undefined
First Attemp (Successful, relationship also created in the "through" table )
[
{
"album_type": "compilation",
"id": "21132268",
"name": "Album One",
"authors": [
{
"name": "Node Js",
"suffix": "Dr."
}
]
}
]
Second Attempt Failed (Having a new Album but with the same author(s))
[
{
"album_type": "compilation",
"id": "23398868",
"name": "Album Two",
"authors": [
{
"name": "Node Js",
"suffix": "Dr."
}
]
}
]
Error:
"Cannot read property 'field' of undefined",....... "TypeError: Cannot
read property 'field' of undefined at
options.defaults.Object.keys.map.name ...........
Here's the DB Functon
var createAlbum = async (albums) => {
try {
// transaction handle
return await models.sequelize.transaction (t => {
// Prepare each album to be created
return Promise.all(albums.map(album => {
return models.Album.findOrCreate({
transaction: t,
where: {name: album.name}, // where: {id: album.id}
defaults: album,
include: ['authors']
})
}))
});
} catch (error) {
// transaction will be rolled back if error
logger.error(error);
throw error;
}
}
Models
//album.js
'use strict';
module.exports = (sequelize, DataTypes) => {
var Album = sequelize.define('Album', {
id: { type: DataTypes.INTEGER, primaryKey: true, allowNull: false },
name: { type: DataTypes.STRING, primaryKey: true, allowNull: false },
album_type: { type: DataTypes.STRING, allowNull: false },
});
Album.associate = (models) => {
models.Album.belongsToMany(models.Author, {
through: 'AlbumAuthor',
as: 'authors',
foreignKey: 'album_id',
otherKey: 'author_name'
})
}
return Album;
}
//author.js
'use strict';
module.exports = (sequelize, DataTypes) => {
var Author = sequelize.define('Author', {
name: { type: DataTypes.STRING, primaryKey: true, allowNull: false },
suffix: { type: DataTypes.STRING, allowNull: true }
});
return Author;
}
//album_author.js
'use strict';
module.exports = (sequelize, DataTypes) => {
var AlbumAuthor = sequelize.define('AlbumAuthor', {
album_id: { type: DataTypes.INTEGER, primaryKey: true, allowNull: false },
author_name: { type: DataTypes.STRING, primaryKey: true, allowNull: false }
});
AlbumAuthor.associate = (models) => {
models.AlbumAuthor.belongsTo(models.Album, {
onDelete: 'CASCADE',
foreignKey: 'album_id',
targetKey: 'id',
}),
models.AlbumAuthor.belongsTo(models.Author, {
onDelete: 'CASCADE',
foreignKey: 'author_name',
targetKey: 'name'
})
}
return AlbumAuthor;
}

Related

Column specified twice error in sequelize

I have defined two table with many-to-many association between them.
create-image-migration.js
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Images', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
name: {
type: Sequelize.STRING
},
...
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Images');
}
};
create-category-migration.js
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Categories', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
name: {
type: Sequelize.STRING
},
...
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Categories');
}
};
Now JOIN table is defined as follows
create-image-category-migration.js
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('ImageCategories', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
imageId: {
type: Sequelize.INTEGER,
allowNull: false,
references: { model: 'Images', key: 'id' }
},
categoryId: {
type: Sequelize.INTEGER,
allowNull: false,
references: { model: 'Categories', key: 'id' }
},
...
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('ImageCategories');
}
};
image-category-model.js
'use strict';
module.exports = (sequelize, DataTypes) => {
const ImageCategory = sequelize.define('ImageCategory', {
imageId: {
type: DataTypes.INTEGER,
allowNull: false,
references: { model: 'Image', key: 'id' },
},
categoryId: {
type: DataTypes.INTEGER,
allowNull: false,
references: { model: 'Category', key: 'id' },
},
...
}, {});
ImageCategory.associate = function(models) {
models.Image.belongsToMany(models.Category, { through: ImageCategory });
models.Category.belongsToMany(models.Image, { through: ImageCategory });
};
return ImageCategory;
};
Now when I run the migration the join table is created with respective column name as specified in migration file i.e. in camel case.
But when I run the following bulkCreate command in sequelize to insert data
await db.ImageCategory.bulkCreate([
{ imageId: 'someId', categoryId: topicId, categoryType: 'topic' },
{ imageId: 'someId', categoryId: styleId, categoryType: 'style' },
]);
I am get the following error:
sqlMessage: "Column 'imageId' specified twice",
sql: "INSERT INTO `ImageCategories` (`imageId`,`categoryId`,`categoryType`,`createdAt`,`updatedAt`,`ImageId`) VALUES (5,'22','topic','2022-11-26 08:11:41','2022-11-26 08:11:41',NULL),(5,'27','style','2022-11-26 08:11:41','2022-11-26 08:11:41',NULL);"
},
As we can see here "ImageId" is automatically added by sequelize. So my question is if there is a convention followed by sequelize to name the column name while creating join table since it is not mention anywhere on its documentation.
By default Sequelize generates foreign key names in the pascal case. You do have foreign keys in the junction table that differ with the letter case.
So you just need to indicate foreign keys explicitly in both associations:
ImageCategory.associate = function(models) {
models.Image.belongsToMany(models.Category, { through: ImageCategory, foreignKey: 'imageId' });
models.Category.belongsToMany(models.Image, { through: ImageCategory, foreignKey: 'categoryId' });
};

Associate in Sequelize not working as intended

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;

Returning full list after post in sequelize

I am using the Sequelize ORM and want to make a post request in order to create what I call a 'card'. The body of the post request would, for example, look like this:
{
"card": {"title": "cardTitle", "link": "cardLink", "categoryId": 1, "userId": 1},
"tags": [{"title": "tagA", "userId": 1}, {"title": "tagB", "userId": 1}, {"title": "tagC", "userId": 1}]
}
After making this post (in the create function), I want the full list of cards to be returned, as seen in the list function. I am not sure how to do this, especially since I am iterating through each individual Card to create a m:m join. Please see the controller below. Thanks!
const Card = require('../models').card;
const Tag = require('../models').tag;
const CardTag = require('../models').card_tag;
const Category = require('../models').category;
module.exports = {
create(req, res) {
Promise.all([
Card.create(
{
title: req.body.card.title,
link: req.body.card.link,
categoryId: req.body.card.categoryId,
userId: req.body.card.userId
},
{returning: true}
),
Tag.bulkCreate(req.body.tags, {returning: true})
])
.then(([Card, Tag]) =>
Tag.map(tag =>
CardTag.create({cardId: Card.id, tagId: tag.id})
),
// How do I instead list all the Cards, just like I do in the list function below?
res.status(201).send({message: "Card created"})
)
.catch(error => res.status(400).send(error));
},
list(req, res) {
return Card
.findAll({
attributes: ['id', 'title', 'link', 'createdAt', 'updatedAt'],
include: [
{model: Category, attributes: ['title']},
{model: Tag, as: 'tag', attributes: ['title'], through: {attributes: []}}
]})
.then(cards => res.status(200).send(cards))
.catch(error => res.status(400).send(error));
}
};
Models:
card:
'use strict';
module.exports = (sequelize, DataTypes) => {
const Card = sequelize.define('card', {
title: {
type: DataTypes.STRING,
allowNull: false
},
link: {
type: DataTypes.STRING,
allowNull: false
},
categoryId: {
type: DataTypes.INTEGER,
allowNull: false
},
userId: {
type: DataTypes.INTEGER,
allowNull: false
}
});
Card.associate = (models) => {
Card.belongsToMany(models.tag, {through: 'card_tag', as: 'tag'});
Card.belongsTo(models.category);
Card.belongsTo(models.user);
};
return Card;
};
card_tag:
'use strict';
module.exports = function(sequelize, DataTypes) {
const CardTag = sequelize.define('card_tag', {
cardId: DataTypes.INTEGER,
tagId: DataTypes.INTEGER
});
return CardTag;
};
category:
'use strict';
module.exports = (sequelize, DataTypes) => {
const Category = sequelize.define('category', {
title: {
type: DataTypes.STRING,
allowNull: false
}
});
Category.associate = (models) => {
};
return Category;
};
tag:
'use strict';
module.exports = (sequelize, DataTypes) => {
const Tag = sequelize.define('tag', {
title: {
type: DataTypes.STRING,
allowNull: false
},
userId: {
type: DataTypes.INTEGER,
allowNull: false
}
});
Tag.associate = (models) => {
Tag.belongsToMany(models.card, { through: 'card_tag', as: 'card'});
Tag.belongsTo(models.user);
};
return Tag;
};

Sequelize join data in tree

I have 3 models that work like a tree: Plants, Genre and family.
Each family can have a lot of genres each genre is associated to 1 family.
Same for Genre, each 1 can have a lot of plants and 1 plant can have 1 genre.
So based on that, i have this models:
Plant
"use strict";
var sequelize = require('./index');
var bcrypt = require('bcrypt-nodejs');
var User = require('./User');
module.exports = function (sequelize, DataTypes) {
var Plant = sequelize.define("Plant", {
specie: {
type: DataTypes.STRING,
allowNull: false,
},
description: {
type: DataTypes.TEXT,
allowNull: true,
defaultValue: "No description for this plant yet"
},
directory: {
type: DataTypes.STRING,
allowNull: false
},
genreId: {
type: DataTypes.INTEGER,
allowNull: true
}
},
{
associate: function (models) {
Plant.hasMany(models.Foto, { foreignKey: "plantId", as: 'fotos' });
}
}
);
Genre
module.exports = function (sequelize, DataTypes) {
var Genre = sequelize.define("Genre", {
name: {
type: DataTypes.STRING,
allowNull: false
},
familyId: {
type: DataTypes.INTEGER,
allowNull: true
},
directory: {
type: DataTypes.STRING,
allowNull: false
}
},
{
associate: function (models) {
Genre.hasMany(models.Plant, { foreignKey: "genreId", as: 'plant' });
}
}
);
Family
module.exports = function (sequelize, DataTypes) {
var Family = sequelize.define("Family", {
name: {
type: DataTypes.STRING,
allowNull: false
},
directory: {
type: DataTypes.STRING,
allowNull: false
}
},
{
associate: function (models) {
Family.hasMany(models.Genre, { foreignKey: "familyId", as: 'genre' });
}
}
);
now, i do a query where i want to get all data related to the plant(genre and family) so i pass the id for the plant in the routing, via req.params.id.
after that i try to do a include so i can get the data with eager loading, because i need to get a json with all the data related to the plant.
But i can't get any data related to the other models, just with the specific plant table, any help?
Here is the controller code on the server:
specificPlant: function (req, res, next) {
Plant.findAll({
where: {
id: req.params.id,
},
include: [{ all: true }]
}).then(function (plant) {
console.log(plant);
return res.send(plant);
}).catch(function (err) {
return res.status(400).send({ message: err.stack }); //
})
}
First, define associations that will allow you to get data Plant->Genre->Family
Plant.hasMany(models.Genre, {foreignKey: "genreId", as: 'genre' });
Genre.hasMany(models.Family, { foreignKey: "familyId", as: 'family' });
Then you can query
Plant.findAll({
where: {
id: req.params.id,
},
include: [{
model: Genre,
as: 'genre',
include: [{
model: Family,
as: 'family'
}]
}]
}).then(function (plant) {
//plant
//plant.genre
//plant.genre.family
});

Sequelize association include returns null

I am having an issue when I'm trying to associate a table into my query with sequelize-cli.
My query works but it doesn't populate Adresse table. Only Patient is populated. Adresse array is ignored. (return null)
I made a one-to-one relationship between the tables and am not sure if that's the cause of the error or if it is somewhere else where I am associating the two tables.
here is my models :
server/models/patient.js
module.exports = (sequelize, Sequelize) => {
const Patient = sequelize.define('Patient', {
///
}, {
classMethods: {
associate: (models) => {
Patient.belongsTo(models.Adresse, {
foreignKey: 'adresseId',
});
}
}
});
return Patient;
};
server/models/adresse.js
module.exports = function(sequelize, Sequelize) {
const Adresse = sequelize.define('Adresse', {
adresse: {
type: Sequelize.STRING,
allowNull: false,
},
complementAdr: {
type: Sequelize.STRING
},
codePostal: {
type: Sequelize.INTEGER,
allowNull: false
},
}, {
classMethods: {
associate: (models) => {
Adresse.hasMany(models.Patient, {
foreignKey: 'adresseId',
as: 'Patients',
});
}
}
});
return Adresse;
};
and here is where I specified the association on my migration files :
server/migrations/20170326145609-create-patient.js
adresseId: {
type: Sequelize.INTEGER,
references: {
model: 'Adresses',
key: 'id_adresse',
as: 'adresseId',
},
},
server/migrations/20170326145502-create-adresse.js
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Adresses', {
id_adresse: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
adresse: {
type: Sequelize.STRING,
allowNull: false,
},
complementAdr: {
type: Sequelize.STRING
},
codePostal: {
type: Sequelize.INTEGER,
allowNull: false
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: function(queryInterface, Sequelize) {
return queryInterface.dropTable('Adresses');
}
};
and finally here is my query on my controller file :
server/controllers/patients.js
const express = require('express');
const router = express.Router();
const jwt = require('jsonwebtoken');
const Patient = require('../models').Patient;
const Adresse = require('../models').Adresse;
module.exports = {
create(req, res) {
return Patient
.create({
///
adressesId: {
adresse: req.body.adresse,
codePostal: req.body.codePostal,
}
}, {
include: [{
model : Adresse
}]
})
.then(patient => res.status(201).send(patient))
.catch(error => res.status(400).send(error));
}
};
Try using Adresse instead adresseId when eager creating the Adresse model instance related to given Patient
return Patient.create({
// patient attributes,
Adresse: {
adresse: req.body.adresse,
codePostal: req.body.codePostal
},
include: [ Adresse ]
}).then(patient => {
// look at the query generated by this function
// it should create both patient and adresse
});

Categories