Model Relationships: Free Ruby on Rails Tutorial

Dive into this detailed tutorial on Ruby on Rails, where you'll learn to create models for cast members and genre, add objects to these models, update views, and navigate multiple exercises for a practical understanding of the framework.

This exercise is excerpted from Noble Desktop’s past web development training materials. Noble Desktop now teaches JavaScript and the MERN Stack in our Full Stack Development Certificate. To learn current skills in web development, check out our coding bootcamps in NYC and live online.

Topics covered in this Ruby on Rails tutorial:

Creating a model for cast members, Adding objects to the cast members model in Rails Console, Updating views to include the cast members model, Creating a genre model, Adding a genre field to the edit form

Exercise Preview

preview model relationship

Exercise Overview

The Editorial Department at Flix has asked us to add two bits of information to every movie’s page: cast members and genre. What are the data requirements for this assignment? For cast members, we need to be able to support multiple cast members per film.

Full-Stack Web Development Certificate: Live & Hands-on, In NYC or Online, 0% Financing, 1-on-1 Mentoring, Free Retake, Job Prep. Named a Top Bootcamp by Forbes, Fortune, & Time Out. Noble Desktop. Learn More.

As for genre, the Editorial Department wants each genre to be clickable, leading to a separate page that lists all the films in a given genre. That’s a tall order, but with our extensive knowledge of models, we can handle it!

  1. If you completed the previous exercises, you can skip the following sidebar. We recommend you finish the previous exercises before starting this one. If you haven’t finished them, do the following sidebar.

    If You Did Not Do the Previous Exercises (3A–5A)

    1. Close any files you may have open.
    2. Open the Finder and navigate to Class Files > yourname-Rails Class
    3. Open Terminal.
    4. Type cd and a single space (do NOT press Return yet).
    5. Drag the yourname-Rails Class folder from the Finder to the Terminal window and press ENTER.
    6. Run rm -rf flix to delete your copy of the Flix site.
    7. Run git clone https://bitbucket.org/noble-desktop/flix.git to copy the Flix git repository.
    8. Type cd flix to enter the new directory.
    9. Type git checkout 5A to bring the site up to the end of the previous exercise.
    10. Run bundle to install any necessary gems.
    11. Run yarn install --check-files to install JavaScript dependencies.

Getting Started

  1. Open the Finder and navigate to Class Files > yourname-Rails Class

  2. Open Terminal.

  3. Type cd and a single space (do NOT press Return yet).

  4. Drag the flix folder from the Finder to the Terminal window.

  5. Make sure you’re in Terminal and hit Return to change into the new folder.

  6. Type the following in Terminal:

    rails server
    
  7. Let’s check that everything is working on the site. Switch to a browser and navigate to localhost:3000 to make sure it’s up and running.

  8. Back in Terminal hit Ctrl–C to shut down the server.

Creating a Model for Cast Members

  1. How can we create a field for cast members that has an unknown number of values? There are several possible solutions, but one of the simplest is to just create a new model. To do that, switch to the Terminal.

  2. Type the following, making sure to double-check the code before hitting Return:

    rails generate model cast_member name:string movie:references
    

    These are all of the fields that will be in the new cast_member model. Pretty straightforward! The references field is the only field type that we haven’t seen before. This will tell Rails that we want the new model to relate back to the movies model.

  3. We suggest opening the flix folder in your code editor if it allows you to (like Sublime Text does).

  4. In your code editor, open flix > db > migrate > #_create_cast_members.rb (the # stands for the unique 14-digit timestamp in the filename).

  5. Take a look at line 5 in this automatically generated file:

    t.references :movie, null: false, foreign_key: true
    

    We typed the references bit ourselves, of course, but Rails added foreign_key: true all on its own. This line is crucial to establishing a relationship between the movies model and the cast_members model. Because the two will be associated, the two databases must have a consistent system to keep the records straight, especially if the databases start holding large numbers of records down the line.

    Also note the null: false bit. This means that each cast member must be attached to a movie.

  6. Close the file, as we’re done checking it out.

  7. Switch to the Terminal and type the following to apply the migration:

    rails db:migrate
    
  8. Switch to your code editor.

  9. Open app > models > cast_member.rb

    When we created the movie model file earlier, it was completely empty—whereas this file has a line automatically added by Rails that reads: belongs_to :movie

  10. This is great, but to get all the Rails magic working and set up a properly functioning model relationship, we need to ensure that the previously-created movie model knows about the cast_member model, too! Open app > models > movie.rb

  11. Add the following bold code around line 5, above the scope:

    validates :runtime, numericality: true
    
    has_many :cast_members
    
    scope :with_placement, -> (placement) { where(placement: placement) }
    

    This is telling the movie model that it has multiple cast members, thus satisfying the data requirement that each movie is able to have any number (zero or more) cast member objects associated with it.

  12. Save the file.

Adding Records to the Cast Members Model in Rails Console

  1. Switch to the Terminal.

  2. Type the following to start up the Rails console:

    rails console
    

    We could create a form to add cast members, but for additional practice (and because it’s way faster) let’s use the Rails console to add cast members to the database. First, let’s look up a movie.

  3. Type the following:

    movie = Movie.find(1)
    

    This will return Text M for Murder. Let’s add some cast members.

  4. Type:

    movie.cast_members.new(name: "John Jones")
    movie.cast_members.new(name: "Susan Shine")
    movie.cast_members.new(name: "Ed Kovac")
    movie.save
    

    Terminal will return a bit of SQL, letting us know that three things have been inserted into cast_members and that all of them are associated with "movie_id": 1. Looking good so far.

  5. Type the following to see which cast members belong to Text M for Murder:

    movie.cast_members
    

    This will return a messy, dense bit of code; if you look carefully you’ll see that there are three cast member records. There’s an easier way to count them.

  6. Type:

    movie.cast_members.count
    

    Terminal will print 3. We can also start referring to these cast members directly.

  7. Type the following:

    movie.cast_members.first.name
    

    Terminal will print John Jones. (Note that it’s the first cast member’s full name, not the first name of a cast member.) We can also access cast members with arrays.

  8. Type:

    movie.cast_members[1].name
    

    Because the count starts with zero, this will return Susan Shine.

    NOTE: You may have noticed that Rails uses capitalized class names like CastMember when referring to the class by name, and a lowercase, underscored version cast_members in other contexts, such as referring to a method. This can be confusing until you get familiar with the convention. It’s just a weird idiosyncrasy that Rails seamlessly switches back and forth between the two.

  9. Try typing the following:

    susan = CastMember.find_by(name: "Susan Shine")
    

    Terminal will print all her information. This is a good way of finding her record without knowing her id number.

  10. Type the following:

    susan.movie.title
    

    Terminal will print Text M for Murder. This helps elucidate why one model gets has_many and another model gets belongs_to.

  11. In the diagram shown below, notice that one model has a reference (often called a foreign key) as rendered in italic text, and that model belongs to (belongs_to) the model that has_many. Therefore, cast_member with its foreign key movie_id belongs to movie. Meanwhile, movie has many (has_many) cast members! We are going to get more familiar with this concept over time, so don’t worry if it’s a bit baffling at first.

    relationship diagram has_many

    The foreign key refers to the field we created when we generated the cast_member model in this way:

    rails generate model cast_member name:string movie:references
    

    This created a field called movie_id which is the foreign key. It is called “foreign” because it refers to another table, and called a “key” because it refers to that table’s primary key (id).

  12. We can create multiple records at once by passing an array of hashes. Let’s add the cast to Planet of the Apps (which has an id of 2) by typing:

    Movie.find(2).cast_members.create([{name: "Mark Wallburg"}, {name: "Hellova Carter"}, {name: "Tim Rath"}])
    

    Remember that the create command performs the new and save actions in one fell swoop. Compared to the way we added cast members to Text M for Murder, using create and passing an array of hashes saved a lot of work.

  13. To save you even more work, we’ve typed up cast members for the rest of the movies. Switch to the Finder.

  14. Navigate to and open the following file, which will probably open in TextEdit:
    Class Files > yourname-Rails Class > flix snippets > cast_members.txt

  15. Hit Cmd–A to select all the contents of the file and copy them (Cmd–C).

  16. Close the file and switch to the Terminal.

  17. Paste (Cmd–V) the text, then press Return once to apply the command.

    Great, the cast members should all be added! Because they were added with create(), the records are saved automatically.

  18. Type the following to exit Rails console:

    exit
    

Updating Views to Include the Cast Members Model

  1. Let’s update the views to display cast members while using a model method to help keep the code DRY. Switch to your code editor.

  2. Open app > models > movie.rb

  3. Add the following bold code at the bottom of your code:

    def cast
       cast_members.map { }
    end
    
    end
    
  4. So far, the method simply takes cast members and collects them. Flesh out the method by adding the following bold code:

    cast_members.map { |c| c.name }.join(", ")
    

    This takes the cast member’s name and joins it with a comma so that lists of multiple cast members will be formatted properly.

  5. Let’s make sure that in the case that the cast_members value is nil, the page won’t throw an error. Add an unless condition as shown in bold:

    cast_members.map { |c| c.name }.join(", ") unless cast_members.nil?

  6. Save the file.

  7. Open app > views > movies > index.html.erb

  8. Find the following code, which should start around line 21:

    <h3><%= movie.title %></h3>
    <div><%= movie.mpaa_rating %>, <%= movie.runtime_hours %></div>
    
  9. Add the following bold code:

    <h3><%= movie.title %></h3>
    <div><%= movie.cast %></div>
    <div><%= movie.mpaa_rating %>, <%= movie.runtime_hours %></div>
    
  10. Save the file.

  11. Open app > views > movies > show.html.erb

  12. Add the following code around line 7:

    <h2><%= @movie.title %></h2>
    <p>Cast: <%= @movie.cast %></p>
    <p class="mpaa-rating">Rating: <%= @movie.mpaa_rating %></p>
    
  13. Save the file.

  14. Type the following in Terminal:

    rails server
    
  15. Switch to a browser and navigate to localhost:3000/movies

    Notice that the cast members are now listed beneath the poster images! Click on any movie to view its detail page; cast members are displaying there, too.

  16. Switch back to Terminal and hit Ctrl–C to shut down the server.

Creating a Genre Model

Creating a genre model is going to be a little more complicated. First we need to figure out where the foreign key should go. In this case, each genre (Horror, Action, Drama) only appears once, but it applies to many (has_many) movies. And, each movie only has (belongs_to) one genre. It sounds like movie needs the foreign key in this situation.

  1. In Terminal, create the genre model by typing the following:

    rails generate model genre name:string
    

    This model only has a single field (name) because the foreign key is going to go in the movies model; in fact, let’s add it now.

  2. Generate a migration by typing:

    rails generate migration add_genre_id_to_movies genre:references
    
  3. There’s one more change we need to make before applying this migration. Open the newly-created migration file and amend this line:

    add_reference :movies, :genre, null: false, foreign_key: true
    

    Remove null: false, so it looks like this:

    add_reference :movies, :genre, foreign_key: true
    

    Why did we have to do this? We already have several movies in the database, and we don’t want to assign a default genre. We will get an error if we try to apply this migration because those records will have a NULL value for genre_id by default.

  4. Save the file.

  5. Type the following to apply the migration:

    rails db:migrate
    

    We need to make sure that relationship information is added to both models.

  6. Switch to your code editor.

  7. Open app > models > genre.rb

  8. Add the following bold code:

    class Genre < ActiveRecord::Base
       has_many :movies
    end
    
  9. Save the file.

  10. Open app > models > movie.rb

  11. Add the following code around line 7:

    has_many :cast_members
    belongs_to :genre, optional: true
    

    Note that we are adding optional: true here. This makes the genre field optional for movies. Otherwise, existing movies without a genre will throw an error.

You might be thinking that it sounds a little funny to say that movie belongs to genre. In terms of how we think about our data, movies are WAY more important than their genre! It’s just one of those things you have to get used to—belongs_to, at the end of the day, exists to tell Rails which model has the foreign key.

  1. Save the file.

  2. Switch to Terminal.

  3. Let’s use Rails console to add a few genres quickly. Type the following:

    rails console
    
  4. Type the following to create a few genres:

    Genre.create(name: "Horror")
    Genre.create(name: "Drama")
    Genre.create(name: "Sci-Fi")
    

    This range of genres is pretty simplistic, but it should encompass the handful of films that we have on Flix so far.

  5. Type the following to get out of the Rails console:

    exit
    

Re-purposing the Play Trailer Button as an Edit Button

Now that we have created the genre model, related it to movies and made a few genres­, we’ll start assigning genres to movies. Let’s get some more practice adding additional models to browser-accessible forms by adding genres to the movie edit form.

Before we do that, there’s something we’ll have to address. It will be tiresome to go to localhost:3000/movies/1/edit and then localhost:3000/movies/2/edit and so on for all of the movie records. Instead, let’s re-purpose the Play Trailer button on the movie detail page and make it an Edit button.

  1. Switch to your code editor.

  2. Open app > views > movies > show.html.erb

  3. Find the following code, around line 10:

       <%= simple_format @movie.description %>
       <a href="#" class="button">Play Trailer</a>
    </div>
    
  4. Replace the Play Trailer line of code with the following bold line of code:

       <%= simple_format @movie.description %>
       <%= link_to "Edit This Movie", edit_movie_path(@movie) %>
    </div>
    

    By passing edit_movie_path to the instance variable @movie, Rails will know—based on the movie object itself—which movie the edit_movie_path should lead to! Pretty cool, huh?

  5. Still editing the same line, add the following bold code to preserve the CSS class and styling of the button:

       <%= simple_format @movie.description %>
       <%= link_to "Edit This Movie", edit_movie_path(@movie), class: "button" %>
    </div>
    
  6. Save the file.

  7. Switch to the browser, navigate to localhost:3000/movies and click on any of the detail pages to admire the handsome Edit This Movie button that is present on every page! The button should lead to the correct path, too.

Adding a Genre Field to the Edit Form

  1. Switch to your code editor and open views > movies > _form.html.erb

  2. We need to add the genre dropdown menu to this form. Add the code as follows, around line 15:

       <%= f.text_area :description, cols:60, rows:10 %>
    </p>
    <p>
       <%= f.label :genre_id, "Genre" %>
    </p>
    <p>
       <% f.label :has_subtitles %>
    

    Instead of using collect, we are about to use a Rails method called collection_select and let that do the heavy lifting for us.

    The collection_select method expects the following (in order):

    • The name of the field
    • The collection to be used
    • The value method (the value we are going to store in the database)
    • The text method and any options
  3. The collection_select method might sound confusing for the moment, but let’s see what it looks like. Add the following bold code:

    <p>
       <%= f.label :genre_id, "Genre" %>
       <%= f.collection_select :genre_id, Genre.all, :id,
    </p>
    

    So far we have the name of the field (:genre_id), the collection itself Genre.all (that is, all instances of the genre class) followed by the name of the value we want to store in the database (the value method) which is :id because we want to take the id of the selected genre and save that in the genre id field for this model.

  4. Add the following bold code to the collection_select method:

    <%= f.collection_select :genre_id, Genre.all, :id, :name, include_blank: true %>
    

    We’ve just added :name (the text method) which is what we want to display in the menu. Finally, in the spot for any additional options, we’ve added include_blank: true, which ensures that no genre is selected by default and, in the case that users do not wish to select a genre, the field could be left blank.

  5. Save the file.

  6. Open app > controllers > movies_controller.rb

  7. Scroll down to the bottom of the file. Inside the private movie_params method in the long line of code, add the following bold code (keeping it on one line):

    movie_params = params.require(:movie).permit(:title, :description,
    :has_subtitles, :placement, :mpaa_rating, :release_date, :ticket_price, :runtime,
    :poster_image, :director, :genre_id)

    If you forgot to add this to permit, you could send the information through the form but it would not be saved!

  8. Save the file.

  9. Type the following in Terminal:

    rails server
    
  10. Switch to a browser and navigate to localhost:3000/movies/1

  11. Click on the Edit This Movie button.

  12. Select Horror as the genre.

  13. Click Update Movie.

    You should now be back to the detail page for Text M for Murder. We assigned a genre, but notice it’s not appearing on the details page! Let’s fix that. Keep the page open in your browser so you can reload it in a moment.

  14. Switch to show.html.erb in your code editor.

  15. Add the following code around line 15:

    <h4>Movie Details</h4>
    <div>
       <span>Genre:</span>
       <%= @movie.genre.name %>
    </div>
    <div>
       <span>Director:</span>
    
  16. Add an unless statement to avoid errors in the case the genre value is nil:

    <h4>Movie Details</h4>
    <div>
       <span>Genre:</span>
       <%= @movie.genre.name unless @movie.genre.nil? %>
    </div>
    <div>
       <span>Director:</span>
    
  17. Save the file.

  18. Switch back to the browser and reload the movie detail page for Text M for Murder. The Genre should be displaying properly in the Movie Details sidebar. Great!

  19. Use the Edit This Movie button on each of the movie detail pages to set the following genres for the four remaining movies:

    Planet of the Apps: Sci-Fi
    The Typographer’s Wife: Drama
    Gone with the Windows: Drama
    Will Code for Brains: Horror
  20. Back in Terminal hit Ctrl–C to shut down the server.

    In the next exercise we will work on creating a genre view to display all the movies in a given genre on a special page.

Noble Desktop Publishing Team

The Noble Desktop Publishing Team includes writers, editors, instructors, and industry experts who collaborate to publish up-to-date content on today's top skills and software. From career guides to software tutorials to introductory video courses, Noble aims to produce relevant learning resources for people interested in coding, design, data, marketing, and other in-demand professions.

More articles by Noble Desktop Publishing Team

How to Learn Coding

Master coding with hands-on training. Learning how to code in JavaScript, Python, and other popular languages can pave the way to a job in tech, such as web development, data science & analytics, or software engineering.

Yelp Facebook LinkedIn YouTube Twitter Instagram