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
Director.where({ :id => m.director_id }).at(0) d =
Wouldn’t it be great if I could just type
d = m.director
and 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:
:director, { :class_name => "Director", :foreign_key => "director_id" }) belongs_to(
This line tells Rails:
belongs_to
: We only one result (not an array of results).:director
: Define a method called.director
for all movie objects.:class_name => "Director"
: When someone invokes.director
on a movie, go fetch a result from the directors table.:foreign_key => "director_id"
: Use the value in thedirector_id
column 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)
end
Either 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.director
Even 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:
:director) belongs_to(
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
Hash
as the second argument tobelongs_to
and spell it all out::owner, { :class_name => "User", :foreign_key => "poster_id" }) belongs_to(
This would give us a method called
.owner
that returns aUser
, even though the foreign key column is calledposter_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
Movie.where({ :director_id => d.id }) f =
Wouldn’t it be great if I could just type
f = d.movies
and 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:
:movies, { :class_name => "Movie", :foreign_key => "director_id" }) has_many(
This line tells Rails:
has_many
: We want many results in an array.:movies
: Define a method called.movies
for all director objects.:class_name => "Movie"
: When someone invokes.movies
on a director, go fetch results from the movies table.:foreign_key => "director_id"
: Use thedirector_id
column of the movies table to filter using theid
of 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 })
end
Either 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.movies
Even 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:
:movies) has_many(
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
Hash
as the second argument tobelongs_to
and spell it all out::filmography, { :class_name => "Movie", :foreign_key => "director_id" }) has_many(
This would give us a method called
.filmography
that returnsMovie
s. 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
:characters)
has_many(:actors, { :through => :characters, :source => :actor })
has_many(end
class Character < ApplicationRecord
:movie)
belongs_to(:actor)
belongs_to(end
class Actor < ApplicationRecord
:characters)
has_many(:movies, { :through => :characters, :source => :movie })
has_many(end