I am using Sequelize (new to ORM's) and currently have three tables: card and tags, that have a many-to-many relationship that is established with the third table, card_tags. My question is two fold:
How do I seed data across associations?
How do I create new data (i.e. an API function) that establishes new data across associated data bases?
Please see the below for my models and let me know if I'm leaving anything out. Thanks!
cards model:
'use strict';
module.exports = (sequelize, DataTypes) => {
const Card = sequelize.define('card', {
title: {
type: DataTypes.STRING,
allowNull: false
},
link: {
type: DataTypes.STRING,
allowNull: false
}
});
Card.associate = (models) => {
Card.belongsToMany(models.tag, { through: 'card_tag', as: 'tag' });
};
return Card;
};
tags model:
'use strict';
module.exports = (sequelize, DataTypes) => {
const Tag = sequelize.define('tag', {
title: {
type: DataTypes.STRING,
allowNull: false
}
});
Tag.associate = (models) => {
Tag.belongsToMany(models.card, { through: 'card_tag', as: 'card'});
};
return Tag;
};
card_tags model:
'use strict';
module.exports = function(sequelize, DataTypes) {
const CardTag = sequelize.define('card_tag', {
cardId: DataTypes.INTEGER,
tagId: DataTypes.INTEGER
});
return CardTag;
};
The simplest and quickest way to do is to to insert bulk data in each relation.
You can write sequelize queries to bulkInsert keeping in mind that primary key for each foriegn key is created first.
You can use faker npm module to generate random data.
Related
I have a database library. It has 2 tables: Reader and Book. The tables are related to each other by belongToMany. I need to implement a method that gives a book to the user. As I understand it, this data is stored in a link table, but for some reason I don’t have it? How to give a book to a reader?
Data already exists in Reader and Book tables
My models:
const Book = sequelize.define('Book', {
id: {type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true},
title: {type: DataTypes.STRING, allowNull: false},
author: {type: DataTypes.STRING, allowNull: false},
vendorCode: {type: DataTypes.INTEGER, allowNull: false},
year: {type: DataTypes.DATEONLY, allowNull: false},
numberOfCopies: {type: DataTypes.INTEGER}
});
const Reader = sequelize.define('Reader', {
id: {type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true},
fullName: {type: DataTypes.STRING, allowNull: false},
birth: {type: DataTypes.DATEONLY, allowNull: false}
});
const ReaderBook = sequelize.define('ReaderBook', {
});
Reader.belongsToMany(Book, {through: 'ReaderBook'});
Book.belongsToMany(Reader, {through: 'ReaderBook'});
module.exports = {Book, Reader, ReaderBook};
My method:
async issueBook(req, res) {
try {
const {idBook, idReader} = req.body;
const book = await Book.findByPk(idBook);
const reader = await Reader.findByPk(idReader);
if (!reader || !book) {
return res.status(400).json({message: 'Reader does not exist'});
}
await reader.addBook(book, {through: ReaderBook}); //addBook method doesn't exist why?
console.log(readerBook);
return res.json("Test");
} catch (e) {
console.log(e.message);
return res.status(400).json({message: 'Error issuing a book to a reader'});
}
};
First of all, try to pass ReaderBook into belongsToMany relation on creation. Replace it's string name with actual object you've created.
Reader.belongsToMany(Book, {through: ReaderBook });
// no qoutes here ^ ^
Book.belongsToMany(Reader, {through: ReaderBook });
To be more clear you can simplify your code.
As you mentioned you have a Reader entity:
const Reader = sequelize.define('Reader', { ... });
And Book entity:
const Book = sequelize.define('Book', { ... });
You could ommit manual creation of junction table by just passing name of the table as string value.
Reader.belongsToMany(Book, { through: 'ReaderBook' });
Book.belongsToMany(Reader, { through: 'ReaderBook' });
To apply the models to database now we have to call sync on sequelize:
await sequelize.sync({ alter: true });
As you see there's no necessity to define 3d table at all.
Now 'giving' book to a reader would be even easier.
const book = await Book.create();
const reader = await Reader.create();
//or find them by primary keys or whatever else
const book = await Book.findByPk(keyId);
const reader = await Reader.findByPk(readerId);
//here's simple adding book to the reader
await reader.addBook(book);
That's all, you don't even need to pass any 'through' parameters.
I am trying to change an already existing primary key (id) to be changed to autoincrement through Sequelize Migration. Following is what I tried but didnt quite work. I tried removing the constraint, making changes to the column and adding the constraint again.
module.exports = {
up: async (queryInterface, Sequelize) => {
queryInterface.removeConstraint('category', 'category_pkey')
.then(() => {
queryInterface.changeColumn('category', 'id', {
type: Sequelize.DataTypes.INTEGER,
allowNull: false,
autoIncrement: true,
}).then(() => {
queryInterface.addConstraint('category', 'category_pkey', {
fields: ['id'],
type: "primary key",
name: 'category_pkey'
});
})
})
},
async down(queryInterface, Sequelize) {
}
};
I have looked everywhere and couldn't find any clear answers for this.
I have a complex findAll() with many inclusions and each with their own virtual fields.
What I want is to modify the virtual fields of the result, however as it is returning the model instance trying to access the virtual fields returns undefined as they are not in the result yet.
I have tried 'raw: true' but this removes all virtual fields and as my data has nested tables which also have their own virtual fields which I need, I cannot do that.
Example models
var Book = sequelize.define('Book', {
id: {
type: DataTypes.INTEGER,
allowNull: false,
autoIncrement: true,
primaryKey: true
},
title: {
type: DataTypes.STRING
},
author: {
type: DataTypes.STRING
}
//....other columns,
myField: {
type: DataTypes.Virtual,
get() {
return this.getDataValue('title:') + this.getDataValue('author');
})
Getting the data
model.Book.findAll({
limit: 100
})
.then((result) => {
const newBook = result.map(row => {
return {...row, myField: 'setMyOwnValueHere'}
}
return newBook
}
Get model data first : get
model.Book.findAll({
limit: 100
}).then(result => {
const books = result.map(row => {
//this returns all values of the instance,
//also invoking virtual getters
const book = row.get();
book.myField = 'setMyOwnValueHere';
return book;
});
return books;
});
I have some application. There is an Character, which can belong to many group. A group can have many characters and also many ranks. Each Character have specified rank. Ranks differs across the groups.
Character model:
module.exports = (sequelize, DataTypes) => {
let Character = sequelize.define('Character', {
/* attributes */
}, {});
Character.associate = (models) => {
Character.hasMany(models.outfit, { foreignKey: 'owner' });
Character.belongsToMany(models.group, { through: models.groupmember });
};
return Character;
};
Group model:
module.exports = (sequelize, DataTypes) => {
let Group = sequelize.define('Group', {
/* attributes */
}, {});
Group.associate = function (models) {
Group.belongsToMany(models.character, { through: models.groupmember, as: 'members' });
Group.hasMany(models.grouprank);
};
return Group;
};
GroupMember (junction table) model:
module.exports = (sequelize, DataTypes) => {
let GroupMember = sequelize.define('GroupMember', {
/* groupId, characterId are generated by sequelize */
rankId: DataTypes.INTEGER
}, {});
GroupMember.associate = function (models) {
GroupMember.belongsTo(models.grouprank, { foreignKey: 'rankId', targetKey: 'id' });
};
return GroupMember;
};
Group rank model: (doesn't matter much in my question)
module.exports = (sequelize, DataTypes) => {
let GroupRank = sequelize.define('GroupRank', {
// atributes
}, {});
GroupRank.associate = function (models) {
GroupRank.belongsTo(models.group);
GroupRank.hasMany(models.groupmember, { foreignKey: 'rankId' });
};
return GroupRank;
};
I have added rankId column to junction table and I have problem to retrieve Character with all its groups included and also the rank which it have.
My current code, which returns the Character, the groups to which he belongst to, and also ranks, but the ALL ranks which belongs to the group. And I want just the rank with ID which is specified by rankId.
database.character.findById(characterId, {
include: [{
model: database.group,
through: database.groupmember,
include: {
model: database.grouprank,
/* where: { id: '$GroupMember.rankId$' } */
}
}]
}).then(result => {
});
Yep. I know it looks weird little, it lacks CamelCase but however my problem is about something else. I tried to query with where attribute, but Sequelize is parsing the string, so I can't put there column name. I hope the question is understable enough. Thank you for your time!
I think below code finally worked for me. http://docs.sequelizejs.com/class/lib/sequelize.js~Sequelize.html#static-method-literal
database.character.findById(characterId, {
include: [{
model: database.group,
through: database.groupmember,
include: {
model: database.grouprank,
where: database.Sequelize.literal('`Groups->GroupRanks`.`id` = `Groups->GroupMember`.`rankId`')
}
}]
})
edit: Finally I decided to rewrite models as stated here -> FindAll with includes involving a complicated many-to-(many-to-many) relationship (sequelizejs)
I have a feathers api set up using feathers-sequelize to persist to a MySQL database.
I've got the datamodel set up and can see the relevant tables are created.
const Sequelize = require('sequelize');
module.exports = function (app) {
const sequelizeClient = app.get('sequelizeClient');
const orders = sequelizeClient.define('orders', {
id: {
type: Sequelize.UUID,
primaryKey: true,
defaultValue: Sequelize.UUIDV4
}, {
classMethods: {
associate (models) {
this.belongsToMany(models.users, {through: "offers", foreignKey: "orderId", otherKey: "userId"});
}
}
});
return orders;
};
How do I actually create an association of offers? I've tried something like this:
const orders = hook.app.service("orders");
order.offers.push(user.id);
orders.patch(order.id, order);
but it doesn't seem to have any effect
You shouldn't need to patch with a many-to-many. Simply use the Sequelize association method:
order.addUser(user.id)
To return the association you might need to do an order.reaload() in your after hook.