Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sequelize hasMany Join association

I'm expanding my application and I need to join two models I had previously created with Sequelize, they are as follows:

Meal

sequelize.define('meal', {
    mealId: {
        type: DataTypes.INTEGER, 
        primaryKey: true,
        autoIncrement: true
    },
    quantity: {
        type: DataTypes.DECIMAL,
        allowNull: false
    },
    period: {
        type: DataTypes.INTEGER,
        allowNull: false
    }
})

Food

sequelize.define('food', {
    idFood: {
        type: DataTypes.INTEGER,
        primaryKey: true,
        autoIncrement: true
    },
    nameFood: {
        type: DataTypes.STRING,
        allowNull: false
    }
})

I added the following relationship:

db.food.hasMany(db.meal, {as : 'Food', foreignKey : 'idFood'});

This line adds an idFood column on Meal

Quickly explaining what is going on, Food is a table with many foods (duh) like Bread, Rice, Beans, etc. Meal is a table that identifies which food the user has chosen with their details.

Therefore, my understanding was that Meal had many Food (as I added before) but Food didn't require any relationship with Meal, since it just holds data and isn't changed after I first populate it. But when I tried to join them with:

db.meal.findAll({ include : [db.food] }).then(function (meals) {
    console.log(JSON.stringify(meals));
});

I got the following error:

Unhandled rejection Error: food is not associated to meal!

Can anyone explain what I should do? I think it has to do with the relationships, but I couldn't find on the documentation any good explanation as to what I should do.

Thank you!

Edit: Reading the documentation (again), the example makes sense, but I don't think the example is applicable on my situation, because on their example, User has a Task, and Task belongs to User. But on my case, Food doesn't belong to a Meal, because many Meals can have the same Food in different amounts (or for different users).

like image 500
leofontes Avatar asked Jun 28 '16 17:06

leofontes


People also ask

How do I use association in Sequelize?

Creating associations in sequelize is done by calling one of the belongsTo / hasOne / hasMany / belongsToMany functions on a model (the source), and providing another model as the first argument to the function (the target). hasOne - adds a foreign key to the target and singular association mixins to the source.

What is hasMany Sequelize?

The Sequelize hasMany() method is used to create a One-To-Many association between two Sequelize models. For example, suppose you have SQL tables named Countries and Cities in your database. These two tables are related where one country row can have many city rows.

How do you JOIN two models in Sequelize?

There are two ways you can create JOIN queries and fetch data from multiple tables with Sequelize: Create raw SQL query using sequelize. query() method. Associate related Sequelize models and add the include option in your Sequelize query method.

How do you create a one-to-many relationship in Sequelize?

To create a One-To-One relationship, the hasOne and belongsTo associations are used together; To create a One-To-Many relationship, the hasMany and belongsTo associations are used together; To create a Many-To-Many relationship, two belongsToMany calls are used together.


Video Answer


2 Answers

The issue is that the relation has to be defined both ways. Currently, Sequelize knows how to get from Food -> Meal because of

db.food.hasMany(db.meal, {as : 'Food', foreignKey : 'idFood'});

but it does not know how to get from Meal -> Food. That's why you have to define the same relation the other way round, like so:

db.meal.belongsTo(db.food, {foreignKey : 'idFood'});

This does not add any new keys since it would define meal.idFood which you defined with your first statement already.

Now you should be able to execute

db.meal.findAll({ include : [db.food] }).then(function (meals) {
    console.log(JSON.stringify(meals)); <-- each array element of meals should have an attribute `food`
});
like image 144
Steffen Langer Avatar answered Nov 15 '22 20:11

Steffen Langer


If you are using an alias on the association ({ as: 'Food' }) you need to use it on the include statement as well.

So it'd be like that:

db.meal.findAll({ 
  include : [{
    model: db.food,
    as: 'Food'
  }]
}).then(function (meals) {
  console.log(JSON.stringify(meals));
});

If an association is aliased (using the as option), you must specify this alias when including the model. Notice how the user's Tools are aliased as Instruments above. In order to get that right you have to specify the model you want to load, as well as the alias

More information here.

like image 23
oleh.meleshko Avatar answered Nov 15 '22 22:11

oleh.meleshko