DEV Community

John Paul Fababaer
John Paul Fababaer

Posted on

Sprint 2: Refactoring Models with belongs_to and has_many

Defining methods inside of our model files allows to a more readable and manageable code. Methods like this: app/models/character.rb

class Character < ApplicationRecord
  def movie
    key = self.movie_id

    matching_set = Movie.where({ :id => key })

    the_one = matching_set.at(0)

    return the_one
  end
end
Enter fullscreen mode Exit fullscreen mode

and this: app/models/movie.rb

class Movie < ApplicationRecord
  validates(:director_id, presence: true)
  validates(:title, uniqueness: true)

  def director
    key = self.director_id

    matching_set = Director.where({ :id => key })

    the_one = matching_set.at(0)

    return the_one
  end
end
Enter fullscreen mode Exit fullscreen mode

allows us to reuse these methods wherever we see fit and enables us to adhere to the DRY principle - do not repeat yourself.

Now what is happening in the two examples above?

  1. Both of these methods are Many-to-One relationship in which the "Many" (i.e. Character and Movie) uses its foreign key (i.e. movie_id and director_id, respectively) in order to query the "One" database (i.e. Movie and Director, respectively).

  2. These association accessor methods follow the same pattern... so what does Rails do? MAKE IT EASIER!

We can use the meta-method belongs_to() to make direct associations and define our association accessor using three arguments (could really be ONE if you carefully plan it beforehand!)

Here's how it looks: app/models/character.rb

class Character < ApplicationRecord
  belongs_to(:movie, class_name: "Movie", foreign_key: "movie_id")

  # def movie
  #   key = self.movie_id

  #   matching_set = Movie.where({ :id => key })

  #   the_one = matching_set.at(0)

  #   return the_one
  # end
# ...
end
Enter fullscreen mode Exit fullscreen mode

What is happening?

  1. belongs_to requires THREE arguments:

    First argument: name of the method -> a Symbol that is carefully named to describe the purpose of the method (think carefully!)

    Second argument: the first key/value pair of a Hash with the Key as a Symbol "class_name". The value associated with this Key must be a String that matches the name of the database we want to query.

    Third argument: the second key/value pair of the Hash with the Key as a Symbol "foreign_key". The value associated with this Key must be a String that indicates the foreign_key value used to connect the "Many" database to the "One" database.

  2. In the example above, we defined a method "movie" that wants to query the database "Movie" for any Movie instances that match the movie_id value.

  3. Shortcut: if the values for both of your Keys matches the name of the method, we can short-hand the belongs_to like this:

belongs_to(:movie)
Enter fullscreen mode Exit fullscreen mode

Rails already knows what we mean by this and performs the method as usual.

Using has_many():
We have examined how to use belongs_to() in order to handle the Many-to-One relationship. Now we need to deal with the other side of this relationship, One-to-Many.

Here is an example: app/models/movie.rb

class Movie < ApplicationRecord
  validates(:director_id, presence: true)
  validates(:title, uniqueness: true)

  belongs_to(:director)
  has_many(:characters, class_name: "Character", foreign_key: "movie_id")

  # def characters
  #   key = self.id
  #   the_many = Character.where( {:movie_id => key })
  #   return the_many
  # end
end
Enter fullscreen mode Exit fullscreen mode

What is happening?

  1. In this method, we are now dealing with "One" movie that can have "Many" characters. Using the belongs_to() method is NOT sufficient to create this direct association (behind the scenes, has_many() functions differently despite the same format).

  2. Therefore, it is important to know which side of the relationship the Class is representing:
    -If it's in the "Many" it has to belongs_to "One".
    -If it's in the "One" then it has_many "Many".

  3. Lastly, we can still create the short-hand! As long as it matches, we can turn the code above into:

has_many(:characters)
Enter fullscreen mode Exit fullscreen mode

Hail Rails on Ruby! Isn't that a little bit easier? :)

CY@.

Top comments (0)