Sequelize provides relationship methods to query for data related through M-M relationships
Understand how to set up models and migrations to support M-M relationships with Sequelize
Understand how to query data related through M-M relationships with Sequelize
Understand how to add and query for data in junction tables for M-M relationships
Similar to 1-M relationships, Sequelize provides relationship methods for M-M relationships that make it easy to query related data. Using our Person
and Personality
example from the SQL M-M submodule where each person can have many personalities and vice versa, Sequelize allows us to call methods such as person.getPersonalities()
and personality.getPeople()
to retrieve related data.
The following is Sequelize's official introduction to M-M relationships with Sequelize.
Note the concept of the junction table is central to many to many relationships
In Sequelize's example their junction table is called actor_movies
, but it could also have been called movie_actors
if they wanted. The convention is to name the junction table with both model names concatenated, where the 1st model name is singular and the 2nd is plural. It does not matter which model is 1st and which is 2nd.
Note that we declare an M-M relationship between 2 models with the belongsToMany
association method, where we specify the junction table name in a 2nd parameter with a through
attribute
We will need to create the junction table actor_movies
using a migration because we are not using Sequelize sync
There is no need to define the actor_movies
junction model ourselves unless we plan to include non-foreign-key data in that model. However, we will always need to create the actor_movies
table with a migration because Sequelize will not create it for us.
We will continue with the People
and Personalities
examples from the SQL M-M Relationships submodule.
If the 2 tables we are associating with an M-M relationship do not need to store non-foreign-key data in their junction table, we do not need to create a standalone model for the junction table in our app. However, we will always need to use a migration to create the junction table in our database.
In this section let us assume there is no non-foreign-key data in the junction table and focus on model association methods and the migration for the junction table.
Given Person
and Personality
models, we apply the belongsToMany
association method on each of them toward the other to set up an M-M relationship in Sequelize. Like in the Actor
and Movie
example in the Sequelize official docs above, we declare the through table name person_personalities
in the belongsToMany
method. This also means we need to create a person_personalities
table in our database using migrations.
Now that we've set up the M-M association between Person
and Personality
models, let's create a migration to create the junction table person_personalities
in our database. The following migration assumes we have past migrations that have already created people
and personalities
tables.
Notice there are 2 foreign keys in the person_personalities
junction table, 1 referencing the people
table and the other referencing the personalities
table. Even though the key in the references
object is model
, Sequelize expects the value to be a plural table name.
Just like that, we've set up an M-M association in our app and are ready to start using Sequelize relationships methods on this relationship!
In the Sequelize 1-M Relationships submodule we read about "special methods/mixins added to instances" and ignored the belongsToMany
section. Let's go back and read the belongsToMany
section to understand what relationship methods Sequelize provides to models associated with belongsToMany
.
Probably the most common relationship method we will use is fooInstance.getBars()
, simply getting all the instances of the related table associated with the current instance. For example, person.getPersonalities()
.
No need to worry about retrieving join table attributes in this section. We will explore that in the "Handling non-foreign-key data in junction table" section below.
Note the large number of relationship methods we can call on model instances with a M-M relationship, including methods to count the number of related model instances, check existence of related model instances, set, add, remove, create related model instances. We will likely not need to use all of these, but good to keep in mind in case using them can simplify our code logic!
We can generally assume that Sequelize will get the plural form of our model names right. For example, we can assume that the Sequelize relationship method to get personalities associated with a Person
instance will be Person.getPersonalities()
and not something like Person.getPersonalitys()
.
When we wish to store and retrieve non-foreign-key data in our junction table and access that data from our apps, we will need to explicitly create a new model in our apps for the junction model. Once we've created the junction model and junction table migration we can discuss how to query the junction model in our apps.
We must explicitly define the junction table model if we wish to query non-foreign-key data in the junction table. We name the model the singular of the table name, i.e. since the table name was person_personalities
, the model is PersonPersonality
.
In the model is the intensity
attribute that accompanies each person's personality trait. We also include an id
attribute, typically omitted in models that we will include here to enable querying our junction table (more on this below). We also include the foreign keys to People
and Personalities
tables respectively.
Notice there are 2 belongsTo
association methods to Person
and Personality
models respectively. Recall belongsTo
associations are typically used with 1-M relationships. We included these because we want to declare 1-M associations between both Person
and PersonPersonality
and between Personality
and PersonPersonality
. We will explore why we add these 1-M relationships in the next section.
To complete the 1-M relationships between Person
and PersonPersonality
and Personality
and PersonPersonality
, we will need to add a hasMany
association in both Person
and Personality
models. Below is the same code as before except with the new association.
Reference the person_personalities
migration above on this page. Add an intensity
column to the person_personalities
table with integer data type.
Sequelize has a separate, comprehensive guide to M-M relationships that includes how to manage non-foreign-key data in junction tables.
The content on calling belongsToMany
association methods is the same as what we read above
Note: defining the junction model ourselves allows us to specify non-foreign-key junction model values when calling Sequelize M-M relationship methods such as fooInstance.addBar()
, or in the example on this page, user.addProfile()
.
No need to worry about composite unique key for now, assume id
is still the primary key for the junction table
Having an id
primary key on PersonPersonalities
model is necessary for the "Super Many-to-Many relationship" that allows both Person
and Personality
instances to query both the junction table and the table associated via the M-M relationship. I.e., to allow Person
to query both related PersonPersonality
and Personality
instances, vice versa for Personality
.
Notice that the tables in the database look the same regardless of whether we declare 2 1-M relationships between person
and person_personality
and personality
and person_personality
, or 1 M-M relationship between person
and personality
.
The "Super Many-to-Many relationship" requires us to declare 1-M relationships between the 2 M-M tables and the junction table, and an M-M relationship between the 2 M-M tables. In our person
and personality
example, this means declaring 1-M relationships between person
and person_personality
and between personality
and person_personality
, and an M-M relationship between person
and personality
.
We can ignore everything below "Aliases and custom key names" in the Sequelize M:N Associations guide for now. Those concepts are more advanced and we can come back to them as reference if we need them.
With the "super M-M relationship" we can query our junction model in multiple ways. The cleanest way is to query using M-M relationship methods with the joinTableAttributes
option.
Alternatively we can use 1-M relationship methods to access the junction table, but that may require more acrobatics with Person
and Personality
IDs.
React Select is a React component library that provides an elegant UI for collecting 1-M and M-M relationship data.
belongsToMany
)