Handling models that belong to other models

Often a user needs to submit a database record that belongs to a record in another table. For example, a user may want to submit a review (e.g. "Very scary!") to a specific movie (e.g. "Jaws"). The scaffolded view will have a text field for the foreign key. However, asking users to provide the key id is not reasonable. This page reviews several options.

The examples on this page use the Review model, which belongs to the Movie model.

See the page on model relationships for setting up the models in the database and enabling ActiveRecord to use them.

Using a selection box

A selection box can be used to allow a user to select the item (e.g. Movie) that the record (e.g. Review) belongs to. Using a model-based form with object f (as created in a scaffolded form), a selection box can be created with the select helper:

  <%= f.select :movie_id, [["A.I.", 12], ["Aliens", 18], ["Jaws", 432]] %>

Notice that select helper uses a list of pairs, where each pair consists of what the user sees and what is sent to the server. Usually, it's best to obtain the list of pairs from the database so that the selections are dynamically updated:

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

If the movie_id is already assigned to the Review object, the select box will have that movie showing.

Giving the user no choice

Sometimes the owning object is already established and the user should not get to choose which object it should belong to. For example, the user may first view the page for the movie "Jaws". On this page, the user could select the link Review "Jaws". In this case, the submission form should already have the selection for "Jaws" fixed. Creating this form can be accomplished with a hidden field:

  <%= f.hidden_field :movie_id, @movie.id } %>

Such a form may appear on the show page for a movie, where @movie is a valid Movie object. This tag must appear inside of a form that submits to the Review controller.

Nested routes (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:

   resources :movies do
     resources :reviews
   end

You should also delete the line with resources :reviews.

To submit using a nested route, the submitting form needs to have a valid parent object (e.g. @movie). Then the form declaration appears in the view as follows:

  <%= form_for([@movie, @movie.reviews.new]) do |f| %>

Beginning Rails 3 covers nested resources on pages 161 - 167.