Model Relationships

A record in a data model often has a relationship to one or more records in another model. For example, a web application may have a Movie data model and a Review data model. Since a movie can have several reviews, we say that a review belongs to a review.

Chapter 9 provides a good overview of the content covered on this page. The Rails guide also provides a good reference.

Below are the different types of relationships records with one model can have with records in a different model:

Implementing model relationships

Implementing a model relationship in Rails involves the following steps:

  1. Add a foreign key to the data model (or foreign keys to a join table).
  2. Update the data model class files (in the app/models folder) to explicitly specify the relationships between the two data models.
  3. Update the routes.rb file to support nested routing (optional).
  4. Edit views to allow users to select relationships by name (as appropriate).

Adding a foreign key (for belongs_to relationships)

For Rails applications, the foreign key is only applied to models that have the belongs_to relationship. For example, if a Review belongs to a Movie, then it's the Review model that has the foreign key. Setting up the relationship is easiest if the Rails naming convention is followed for the foreign key. For example, a foreign key in the Review model that references a Movie record should have the name movie_id with a type of integer.

Create a join table (for has_and_belongs_to_many relationships only)

This join table connects the records from one model to records in another model. The join table consists of pairs of foreign keys and should be named using the names of the joined tables separated by an underscore (e.g. recipes_ingredients). For example, recipes can have many ingredients and ingredients can be in many recipes. If the models are called Recipe and Ingredient, each record in the join table (called recipes_ingredients) consists of the fields recipe_id and ingredient_id. Chapter 9 (Creating Tables) shows how to create the needed join table.

Updating the model class files

Explicitly stating the model relationships in the class files allows for easy object-oriented references. Both directions of the relationship should be stated in the files. For example, the movie.rb file should have the following statement:

  has_many :reviews

The review.rb file should have this statement:

  belongs_to :movie

Note the use of the singular or plural when referring to a data model.

There is a good chance that you will want to delete the reviews that belong to a movie if that movie is deleted. You can specify this behavior with the following option in the movie model:

  has_many :reviews, :dependent => :destroy

The book covers other options on p. 90.

Updating the routing rules (optional)

It's possible to update the routing rules so that dependent relationships are explicitly depicted in the URL request. For example, a review belongs to (depends on) a movie. This relationships can be represented in a URL. For example, if the movie has id of 5 and the review has an id of 8, then this nested URL shows the relationship when requesting review 8:

  http://localhost:3000/movies/5/reviews/8

This URL shows that review 8 belongs to movie 5. You can enable this routing scheme by altering the routes.rb file:

   map.resources :students do
     resources :reviews
   end

You should also delete the line with map.resources :reviews.

Object-based references and methods

If the model class files explicitly state the relationships, you can reference model objects from a related object. Here are examples with Movie and Review models (assuming m is a Movie object and r is a Review object):

For views, you might want to allow users to choose the object of the belongs_to relationship when creating a new record. For example, a user might want to write a new review and select the movie from a menu. Here's the needed code for the view:

   <%= f.select :movie_id, Movie.all.collect { |m|
        [m.title, m.id] }