Has Many Through Many Relationships

Matt Eva
4 min readDec 15, 2021

Ruby/ActiveRecord Tutorial

If you’ve worked with relational databases in any capacity, you’re probably familiar with the basic strategy for determining relationships between data in different tables. Is one piece of data in one table connected to many pieces of data in another table? Then you’re dealing with a one-to-many relationship. What if each piece of data in each table has multiple corresponding relationships with data in the other table? Then you’re dealing with a many to many relationship.

This is a great starting point for understanding how to set up relations between different tables in your database, but it’s likely that you’ll need to describe relationships that are a little more complex.

Let’s imagine you’re creating a website that allows people to post, browse, and interact with writing. Users can publish their own work, but they can also like other people’s work, add writing to their libraries, and more. In this framework, users are connected to writing via a variety of different pathways.

First off, they’re directly connected to the writing they themselves publish — this is a one-to-many relationship, where each user has many written pieces (assuming they actually post their own work). Here’s a diagram of what that kind of simple relationship would look like:

But, users also have multiple many-to-many relationships with other people’s writing. When a user adds a piece of writing to their “library”, they’re generating data that links them to that specific piece of writing. Our app will need to access all the items in a user’s library and display it to our user when they visit their library. So, users and writing need another type of relationship.

Let’s start with what we know, and simplify things a bit. Imagine our readers — the people who add items to their library — are separate from our writers. In this case each “reader” would have many creations via their library, while each piece of writing would have many “readers” via different readers’ libraries. This is pretty familiar territory — a many to many relationship. We can set up this type of many-to-many relationship using a join table. Here’s a diagram representing that relationship:

Great, we have two functioning data models — a one to many relationship for our users and their creations, and a many to many relationship for our readers and the creations that they’ve added to their libraries. But, we know that our readers actually are just regular users — so, we’ll have to combine our models.

Now, we have to find a way to distinguish between writing that a user has created and writing that a user wants to access from their library (which they have not created). Fortunately, ActiveRecord gives us a pretty handy method for handling this.

Let’s walk through this example. First off, we see our user’s side of the one-to-many relationship it has with writing — each user has_many :writings. Then we also see the user’s relationship with library_items — each user has_many :library_items. All pretty familiar.

The third line is where things get cool. We can’t say that a user has_many :writings to specify that a user can access writing through library items because we’ve already used :writings to talk about a user’s own writing. Instead, we can come up with a new name — in this case, :writings_in_lib — indicate that a user has_many :writings_in_lib through: library_items and specify the source that we want to reference — in this case source: :writing. Now, we can use writings_in_lib to only access writing that a user has added to their library. Pretty cool, huh? Thanks, ActiveRecord!

Take note that after source:, :writing is singular, not plural. In this case, we’re actually consulting the reference “source” of our library_items, which each belong_to a single piece of writing. So, our writings_in_lib are compiled through: :library_items by looking at the writing_id of each library item.

We can use this method as many times as we want or need to. Let’s imagine that we also want a user to see content that they’ve “liked” or added to a “reading list” of some kind. This is what our data diagram might look like:

And here’s what our ActiveRecord relationships for our user would look like:

Hope that helps you on your backend journey! Happy coding.

--

--