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.

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

In addition to Chapter 9 in the text, here's a nice summary sheet for model relationships.

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. Pages 162 to 165 go through the steps of creating a join table for this relationship.

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.

Updating the routing rules (highly 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, has_many => [ :reviews ]

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.find(:all).collect { |m|
        [m.title, m.id] }