Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Please explain the has_many, through: source: Rails Association

I've found a bunch of articles, stackoverflow answers and rails documentation about 'source:', but none of it explains this association in a way I can understand it. I need the most simplified explanation of this way of associating, if possible.

My example is this:

Album:

    has_many :reviews, :dependent => :destroy
    has_many :reviewers, through: :reviews, source: :user
    belongs_to :user

Review:

    belongs_to :album, optional: true
    belongs_to :user

User:

    has_many :reviews
    has_many :reviewed_albums, through: :reviews, source: :album
    has_many :albums

The rest of the code does not mention "reviewers" or "reviewed_albums", so that is the part I understand the least.

Are those names completely irrelevant?

like image 844
sarah Avatar asked Apr 25 '20 17:04

sarah


People also ask

How do associations work Rails?

Association in Rails defines the relationship between models. It is also the connection between two Active Record models. To figure out the relationship between models, we have to determine the types of relationship. Whether it; belongs_to, has_many, has_one, has_one:through, has_and_belongs_to_many.

What is the difference between Has_one and Belongs_to?

They essentially do the same thing, the only difference is what side of the relationship you are on. If a User has a Profile , then in the User class you'd have has_one :profile and in the Profile class you'd have belongs_to :user . To determine who "has" the other object, look at where the foreign key is.

What is polymorphic association in Rails?

Polymorphic relationship in Rails refers to a type of Active Record association. This concept is used to attach a model to another model that can be of a different type by only having to define one association.


1 Answers

TL;DR

source is standing for the table that this association is referring to, since we give it a different name than just .users because we already have the belongs_to :user association.

Long explanation

I think it's easiest with this little picture which is basically the database schema for the models you posted above.

database schema

We have albums, that belong to users, meaning that a user is basically someone who creates an album. We also have reviews and they belong to albums, meaning an album can be reviewed. And a review is made by a user so that's why a review belongs to a user.

Now associations in rails is a way to create methods that can be called on a database record to find its associated record.

We could get a user from an album or all the reviews a user made for example.

album = Album.find(1)
album.user # => returns the creator of the album

user = User.first
user.reviews # => returns all the reviews a user made

Now there is even more connections between those models than the ones mentioned above.

Let's look at album first:

# album.rb
has_many :reviews, :dependent => :destroy
belongs_to :user
has_many :reviewers, through: :reviews, source: :user

An album belongs to one user who created it. It has many reviews. And, if we we follow the line from albums to reviews and then further along to the users table, we see that we can also access the users that gave the reviews. So we would want to do something like

album.reviews.users

Meaning: give me all the users that left a review for this album. Now this line of code wouldn't work - because album.reviews returns an array (an ActiveRecord::Relation object to be exact) and we cannot just call .users on this. But we can have another association

has_many :reviewers, through: :reviews, source: :user

And here we're calling it reviewers to not get confused with the method/association .user that refers to the creator. Normally, Rails would refer the database table name from the name of the association. Since we're giving a different name here, we have to explicitly give the name of the DB table we're referring to and that is the users table.

So this is what this line is about - we create another association, we don't want the direct line (see image) between album and user, we want the users that left a review on this album, so we go through the reviews table and then we have to give the name (source) of the table so Rails knows in which table to look.

And this will finally allow us to write code like this:

album = Album.first
album.user # => creator of the album
album.reviewers # => all users that have left a review for this album

Hope that helps! Let me know if you have any more questions. Maybe you can explain the other association with source in the users model in the comments.

like image 124
Clara Avatar answered Oct 13 '22 09:10

Clara