A.3 Association Helper Methods
A.3.1 belongs_to
belongs_to(Symbol, Hash) ⇒ ActiveRecord
Let’s say I have a movie in a variable m. It is annoying and error prone to, whenever I want the director associated with a movie, have to type
d = Director.where({ :id => m.director_id }).at(0)Wouldn’t it be great if I could just type
d = m.directorand it would know how to go look up the corresponding row in the directors table based on the movie’s director_id?
Unfortunately, I can’t, because .director isn’t a method that Movie objects automatically know how to perform — the method would be undefined. (Movie objects know how to perform .director_id because we get a method for every column in the table.)
We could define such an instance method in the model ourselves without too much trouble. But, fortunately, since domain modeling and associations are at the heart of every application’s power, Rails gives us a shortcut. Go into the Movie model and add a line like this:
belongs_to(:director, { :class_name => "Director", :foreign_key => "director_id" })This line tells Rails:
- belongs_to: We only one result (not an array of results).
- :director: Define a method called- .directorfor all movie objects.
- :class_name => "Director": When someone invokes- .directoron a movie, go fetch a result from the directors table.
- :foreign_key => "director_id": Use the value in the- director_idcolumn of the movie to query the directors table for a row.
This is exactly what we would do if we defined the instance method by hand:
def director
  return Director.where({ :id => m.director_id }).at(0)
endEither way, we now can utilize this handy shortcut anywhere in our application when we have a movie m and we want to get the record in the directors table associated with it:
m.directorEven better, if you’ve named your method and foreign key column conventionally (exactly matching the name of the other table), you can use the super-shorthand version:
belongs_to(:director)- If you omit specifying the - :class_name, Rails assumes that the table that you want to query is named the same thing as the method you are defining.
- If you omit specifying the - :foreign_key, Rails assumes that the foreign key column is named the same thing as the method plus- _id.
- If either of those things happens to not be true, then just include the - Hashas the second argument to- belongs_toand spell it all out:- belongs_to(:owner, { :class_name => "User", :foreign_key => "poster_id" })- This would give us a method called - .ownerthat returns a- User, even though the foreign key column is called- poster_id. We still have complete control, if we need to depart from conventional method/foreign key names for some reason.
A.3.2 has_many
has_many(Symbol, Hash) ⇒ ActiveRecord_Relation
Let’s say I have a director in a variable d. It is annoying and error prone to, whenever I want the films associated with the director, have to type
f = Movie.where({ :director_id => d.id })Wouldn’t it be great if I could just type
f = d.moviesand it would know how to go look up the corresponding rows in the movies table based on the director’s id?
Unfortunately, I can’t, because .movies isn’t a method that Director objects automatically know how to perform — the method would be undefined.
We could define such an instance method in the model ourselves without too much trouble. But, fortunately, since domain modeling and associations are at the heart of every application’s power, Rails gives us a shortcut. Go into the Director model and add a line like this:
has_many(:movies, { :class_name => "Movie", :foreign_key => "director_id" })This line tells Rails:
- has_many: We want many results in an array.
- :movies: Define a method called- .moviesfor all director objects.
- :class_name => "Movie": When someone invokes- .movieson a director, go fetch results from the movies table.
- :foreign_key => "director_id": Use the- director_idcolumn of the movies table to filter using the- idof the director.
This is exactly what we would do if we defined the instance method by hand:
def movies
  return Movie.where({ :director_id => self.id })
endEither way, we now can utilize this handy shortcut anywhere in our application when we have a director d and we want to get the records in the movies table associated with it:
d.moviesEven better, if you’ve named your method and foreign key column conventionally (exactly matching the name of the other table), you can use the super-shorthand version:
has_many(:movies)- If you omit specifying the - :class_name, Rails assumes that the table that you want to query is named the same thing as the method you are defining.
- If you omit specifying the - :foreign_key, Rails assumes that the foreign key column is named the same thing as this model plus- _id.
- If either of those things happens to not be true, then just include the - Hashas the second argument to- belongs_toand spell it all out:- has_many(:filmography, { :class_name => "Movie", :foreign_key => "director_id" })- This would give us a method called - .filmographythat returns- Movies. We still have complete control, if we need to depart from conventional method/foreign key names for some reason.
A.3.3 has_many/through
has_many(Symbol, Hash) ⇒ ActiveRecord_Relation
After you have established all of your one-to-many association helper methods, you can also add many-to-many helper methods with the :through option on has_many:
class Movie < ApplicationRecord
   has_many(:characters)
   has_many(:actors, { :through => :characters, :source => :actor })
end
class Character < ApplicationRecord
  belongs_to(:movie)
  belongs_to(:actor)
end
class Actor < ApplicationRecord
   has_many(:characters)
   has_many(:movies, { :through => :characters, :source => :movie })
end