Sequelize create with associations - mysql

I have been trying to define a relationship between 3 tables and then create them all in one create function. For some reason, while creating the 3 models, the linking IDs (foreign keys) are undefined and are not passing on. Here are the associations:
Person.js:
models.person.Lead = models.person.hasMany(models.lead, {
onDelete: "CASCADE",
foreignKey: "person_id"
});
Lead.js:
models.lead.Person = models.lead.belongsTo(models.person, {foreignKey: 'person_id'});
models.lead.Sealant_customer = models.lead.hasOne(models.sealant_customer, {
onDelete: "CASCADE",
foreignKey: 'lead_id'
})
sealantCustomer.js:
models.sealant_customer.Lead = models.sealant_customer.belongsTo(models.lead);
The build function:
let sealantCustomer = models.sealant_customer.build({
address: body.address,
city: body.city,
roof_size: body.roofSize,
last_sealed: body.lastSealed,
existingSealant: body.existingSealant,
leaks_freq: body.leaksFrequency,
floor: body.floor,
elevator: body.elevator,
panels: body.panels,
home_type: body.homeType,
urgency: body.urgency,
next_step: body.nextStep,
more_info: body.moreInfo,
lead: {
site,
url: body.url,
date,
ip: body.ip,
person: {
name: body.name,
email: body.email,
phone: body.phone,
date,
city: body.city ? body.city : undefined,
address: body.address ? body.address : undefined,
}
}
}, {
include: [{
model: models.lead,
association: models.sealant_customer.Lead,
include: [{
model: models.person,
association: models.lead.Person
}]
}]
})
The outputted object is good except for the fact that lead_id and person_id are nulls (Each model has its own ID, but not the associated model's id). I also should note there are no validation errors and the data is good.

The library has a bug in the build function as far as I can tell. Same syntax with create worked perfectly.

In Sequelize v6, the association identifier in the include section is not valid. Otherwise, this build function should properly work.

Related

Is it possible to make a second Many-to-many relationship between 2 tables in sequelize?

my goal here is to add a second join table between two tables :
I got one table User
I got one table Tirage
And I already created a join table Participate,
here is the "associations" and Participate model code :
Participate :
module.exports = (sequelize, Sequelize) => {
const Participe = sequelize.define("participe", {
uuid: {
type: Sequelize.UUID,
primaryKey: true,
defaultValue: Sequelize.UUIDV1,
},
cadeauA: {
type: Sequelize.UUID
},
participantId: {
type: Sequelize.UUID
},
tirageId: {
type: Sequelize.UUID
},
isAdmin: {
type: Sequelize.BOOLEAN
},
selfGranted: Sequelize.BOOLEAN
});
return Participe;
};
Association :
db.users.belongsToMany(db.tirages, {through: db.participe, foreignKey:'cadeauA', foreignKey:'participantId'});
db.tirages.belongsToMany(db.users, {through: db.participe, foreignKey:'tirageId'});
(Note that I didn't check yet if the second foreign key in the first line works, so if it's not it's kinda normal).
It works, I can manage to get all tirages from users who are "participating" thanks to :
exports.getUserInvitation = async (req, res) => {
const user = await User.findOne({
where: {
uuid: req.params.participant
},
include: Tirage,
as: 'tirages'
});
return res.status(200).send(user.tirages);
}
So now, I would like to add an other junction table, not to see if a user is participating to a tirage but if he is invited to the tirage.
I tried mutliple things but it just won't work or even break the "participate" feature.
I read the entire association doc from sequelize but nothing is really helpfull for multiple many-to-many association between two tables.
What I tried :
db.users.belongsToMany(db.tirages, {through: db.invitation, foreignKey:'invite', foreignKey:'admin'});
db.tirages.belongsToMany(db.users, {through: db.invitation, foreignKey:'tirageId'});
db.invitations.belongsTo(db.users, {foreignKey:'admin', foreignKey:'invite'});
db.invitations.belongsTo(db.tirages, {foreignKey:'_tirage'});
And many other things that I don't remember.
Edit :
I would like to say that I tested it :
db.users.belongsToMany(db.tirages, {through: db.invitations, foreignKey:'invite', foreignKey:'admin'});
db.tirages.belongsToMany(db.users, {through: db.invitations, foreignKey:'tirageId'});
but when I do
await user.addTirage(tirage, { through: Invitation });
It adds a Participate record (even if I specify the through table).
First, if you indicate the same key foreignKey twice JS will keep only the last one.
Second, Sequelize does not support composite foreign keys.
Third, to distinguish associations from the same tables to the other same tables you need to indicate aliases in associations and also in each query where you need to include associated models.
Let's assume you use one column from each table to join with junction tables then it might look like this:
db.users.belongsToMany(db.tirages, {through: db.participe, foreignKey:'participantId', as: 'participatingTirages'});
db.tirages.belongsToMany(db.users, {through: db.participe, foreignKey:'tirageId', as: 'participatedUsers'});
db.users.belongsToMany(db.tirages, {through: db.invitations, foreignKey:'invite', as: 'invitatingTirages'});
db.tirages.belongsToMany(db.users, {through: db.invitations, foreignKey:'tirageId', as: 'invitedUsers'});
Now, you can add a tirage with invitation like this (maybe I make mistake in the name of the method, try to find out the real one):
await user.addInvitingTirage(tirage);
OR to add a tirage to participate in:
await user.addParticipatingTirage(tirage);

MySQL foreign key name, what is it?

I am using Sequelize, a nodejs ORM for mysql. Using mysql workbench I made an EEM diagram and pushed that design into the db, so far so good.
Now in Sequelize I have to tell it what the design of the DB looks like, part of that is telling it what foreign keys are called.
In Workbench there is a foreign key tab in the tablethere are variables formatted likefd_positions_tradingPLan1` but I never name that, in fact in my EEM diagram I have
Then if I go to that foreign keys tab at the bottom I get this. I am confused as to exactly what I should tell the ORM the foreign key is...
Let's take your positions Table as reference. To build your model on sequelize you have to do the following:
module.exports = (sequelize, DataTypes) => {
const Position = sequelize.define('Position', { // this is the name that you'll use on sequelize methods, not what you have on your db
// define your columns like this:
tradeName: { //the name of the variable that you'll use on sequelize and js
field: 'trade_name', //the actual name of your column on the table
type: DataTypes.STRING(128) // the data type
},
// .......
// for your foreignKeys you have to define the column like your other attributes.
userId: {
field: 'user_id',
type: DataTypes.INTEGER
},
}, {
tableName: 'positions', //this is the name of your table on the database
underscored: true, // to recognize the underscore names
createdAt: 'created_at', //
updatedAt: 'updated_at',
});
//now for your association let's say that you defined your USER table like this example.
Position.associate = (models) => {
// on the foreignKey value, youhave to put the same that you define above, and on the db.
Position.belongsTo(models.User, { as: 'User', foreignKey: 'user_id' });
//depending on your other relations, you are gonna use hasMany, hasOne, belongsToMany
};
return Position;
};
Sequelize does the association only one way, that means that on this example, you can't query with sequelize from User to Position, to be able to
have two way association you have to defined on both models.
User.associate = (models) => {
// on this case we use hasMany cause user can have many positions I suppose, if not, use hasOne
User.hasMany(models.Poisition, { as: 'positions', foreignKey: 'user_id' }); //remeber to use the same foreignKey name
};
UPDATE:
as is an identfier for Sequelize. Let's say you make two associations for the same model, later when you try to query one of this associations, you can specify the association that you want
User.associate = (models) => {
User.hasMany(models.Poisition, { as: 'positions', foreignKey: 'user_id' });
User.hasMany(models.Poisition, { as: 'customerPositions', foreignKey: 'customer_id' });
};
//the actual association call
User.findAll({
include:[{
model: db.user,
as: 'positions'
}, {
model: db.user,
as: 'customerPositions'
}]
})
Now for fk_positions_users1, this is an identifier for MySQL itself. Sequelize only check for the foreignKey and the models involve. Obviously when Sequelize create the reference, it gives a template name using the table and column name. I tried myself creating a new foreignKey on my table and then updating the model and everything goes fine. You should'nt have problems with that.

sequelize multiple foreign key includes only one column

i have made two foreign keys from user table.
db.Subscription.belongsTo(db.User, {foreignKey: 'creatorId'});
db.Subscription.belongsTo(db.User, {foreignKey: 'subscriberId'});
during search query i get subscriberId column included instead of creatorId
Subscription.findAll({
where: {
subscriberId: req.decoded._id
},
include: [
{
model: User,
foreignKey: 'creatorId',
attributes: ['name', 'role', 'uid', 'imageUrl']
}
]
})
can someone please find out what i am doing wrong here.
Try setting a name for the associations so Sequelize has a better idea which association to include. You can do something like this to set the names on the associations...
db.Subscription.belongsTo(db.User, {
as: 'creator',
foreignKey: 'creatorId'
});
db.Subscription.belongsTo(db.User, {
as: 'subscriber',
foreignKey: 'subscriberId'
});
Then you can use those names in the query to get the specific association, as so...
Subscription.findAll({
include: {
model: User,
as: 'creator',
attributes: ['name', 'role', 'uid', 'imageUrl']
},
where: {
subscriberId: req.decoded._identer
}
});
When you have associations to the same table more than once setting a name helps the ORM determine which association to load. For the record, for all of the records that get returned you can access that association by accessing the .creator on the instance.
Good luck! :)

Sequelize : How to map a custom attribute in a pivot table

I've got this pivot table, which represents a many to many relationship with the models Person and Movie.
The thing is I want to get the role when I call the movies that get the persons associated. I tried this but it doesn't show the role :
models.Movie.findAll({
include: [{
model: models.Person,
as: 'persons',
through: {attributes: ["role"]}
}]
}).then(function(movies) {
res.json(movies);
});
Do I have to specify something in the models for the role ?
I finally managed to achieve this by creating a model for the pivot table movie_person with the role attribute as a string.
var MoviePerson = sequelize.define("MoviePerson", {
role: DataTypes.STRING
},
{
tableName: 'movie_person',
underscored: true
});
Then in my Movie model I added this
Movie.belongsToMany(models.Person, {
through: models.MoviePerson,
foreignKey: 'movie_id',
as: 'persons'
});
I had to do something obviously similar to this in my Person model and that's it !
For the purpose of those who will need this, there is a new method called 'magic methods'. I believe you have declared your many-to-many asociation
const movies = Movie.findAll();
const person = Person.findbyPk(personId);
const moviesPerson = movies.getPersons(person);

Unique doesn't work on Node.js Sails.js "sails-mysql"

I've just started to get into the framework of Sails for Node. But it seems like I can't get the unique- requirements to work when adding for example users to the sails-mysql database. I can atm add unlimited number of new users with the same username and email.
From what I have read it should work, I did also try with sails-memory and there this exact code did work. Is it something I have missed out?
module.exports = {
attributes: {
username: {
type: 'string',
required: true,
unique: true
},
firstname: {
type: 'string',
required: true
},
lastname: {
type: 'string',
required: true
},
password: {
type: 'string',
required: true
},
birthdate: {
type: 'date',
required: true
},
email: {
type: 'email',
required: true,
unique: true
},
phonenumber: 'string',
// Create users full name automaticly
fullname: function(){
return this.firstname + ' ' + this.lastname;
}
}
};
As I mentioned above, this does work with the memory-storage. And now I have also tried with mongodb where it does work fins as well.
Got support from Sails.js on twitter: "it uses the db layer- suspect it's an issue with automigrations. Would you try in a new MySQL db?"
This answer did work, a new db and everything was just working :)
Just to add to this, since sails uses auto-migrations, if you initially start the server and your model does not have an attribute as unique, the table is built without the unique (index) switch. If you then change an existing attribute in the model to unique, the table will not be rebuilt the subsequent times you start the server.
One remedy during development is to set migrations in your model to drop like this:
module.exports = {
migrate: 'drop' // drops all your tables and then re-create them Note: You loose underlying.
attributes: {
...
}
};
That way, the db would be rebuilt each time you start the server. This would of course drop any existing data as well.