6

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?

sarah
  • 151
  • 1
  • 7

1 Answers1

10

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.

Jess Alejo
  • 143
  • 8
Clara
  • 2,677
  • 4
  • 16
  • 31
  • 1
    This is amazing, thank you. I don't really know why the user sources to the album... is has_many reviewers and has_many reviewed albums referring to each other because of source? – sarah Apr 25 '20 at 19:49
  • 2
    Don't read it from the back - start from the beginning and read from the start: `has_many: :reviewed_albums`, through: :reviews` - try to follow the lines in the image. The `source: :ablum` is only for Rails :) – Clara Apr 25 '20 at 19:50
  • But can I ask you this... if a User has many reviews and has many albums (through reviews), is that relationship to albums only referring to the albums they write reviews on? – sarah Apr 25 '20 at 21:29
  • 1
    Exactly! The `has_many :reviews` refers to the reviews the user writes herself. And the `has_many :reviewed_albums` refers to the albums she/he reviewed. So then you can do `user.reviewed_albums` – Clara Apr 25 '20 at 21:54
  • Thanks so much for this explanation. It really helped. – sarah Apr 25 '20 at 22:10
  • source is standing for the table, but what for model? How to understand the situation where table and model names are inconsistent? – TPR Feb 09 '22 at 04:08