Build a Simple Movie Reviews Website in Rails 6, part 3
A full website, from installation to deployment
is a developer, technical writer, and a (former) designer.
In the previous article, we’ve added user authentication and authorization with devise, and we’ve set up the Movies model.
In this article, we’ll add the CRUD functionality - create, read, update, delete - for the Movies model.
Image by CodingExercises
Let’s begin by inspecting our database tables using the Rails console:
rails c ActiveRecord::Base.connection.tables
The returned output:
=> ["schema_migrations", "ar_internal_metadata", "users", "movies"]
Alright, let’s inspect the columns in the movies table.
Here’s the output:
Let’s load all the movies; note: we know this will not return anything:
As expected the above ActiveRecord query will map onto an PostgreSQL query, and execute it, returning empty:
Movie Load (0.5ms) SELECT "movies".* FROM "movies" LIMIT $1 [["LIMIT", 11]] => #<ActiveRecord::Relation >
Great, everything in our model seems to work.
Before we can effectively work with the Movie Model, we need to add the movie controller too.
Adding Movie Controller
To add the movie controller, we’ll run this on the command line:
rails g controller Movies index show new create edit update destroy
Similar to how we added a single
home action to the
Pages controller, we’ve added all the needed actions for the Movies controller.
To see the change to our file structure, let’s run
The newest updates are committed as “Add the Movies controller” commit message.
Let’s also do a
git diff --stat to compare the newest commit with the previous one. To know the actual SHAs that we’ll be comparing, we’ll do the
git log --oneline first, then take the two most recent ones.
git diff 30dd5c9 5ab103a --stat
Here’s a screenshot of the output:
movies_controller.rb looks like this:
class MoviesController < ApplicationController def index end def show end def new end def create end def edit end def update end def destroy end end
Let’s update it to this:
before_action :set_movie, only: [:show, :edit, :update, :destroy] def index @movies = Movie.all end def show end def new @movie = Movie.new puts @movie end def create @user = current_user puts @user @movie = @user.movies.create(params_requre) redirect_to movies_path #@movie = @user.movies.new(params_requre) # if movie.save end def edit end def update puts "77777777777777777777" @movie.update(params_requre) redirect_to movies_path end def destroy end def set_movie @movie = Movie.find(params[:id]) puts @movie end protected def params_requre params.require(:movie).permit(:title, :description) end end
Additionally, looking at the Movie model (defined in the
models/movie.rb file) we can see that it defines the relationship between users and movies:
class Movie < ApplicationRecord belongs_to :user end
We also need to define the other side of the relationship, inside the
models/user.rb file, by adding this line just above the closing
end line of the
User class definition.
Now the entire updated
models/user.rb files looks like this:
class User < ApplicationRecord # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable has_many :movies end
Let’s add the changes to movies controller and user model, in a commit titled “Update user model and movies controller”.
Now that we’ve got everything set up, let’s take a quick detour and add a new movie using the rails console.
Because it’s faster to do it this way than having to add the code to all the views that we’ve added when the movies controller was generated. Currently, all these view files are just empty slots waiting to be filled with actual code. Right now, they’re just static HTML, like, for example, the
<h1>Movies#create</h1> <p>Find me in app/views/movies/create.html.erb</p>
Let’s also see how the generated movies controller affected the contents of the routes.rb file:
Rails.application.routes.draw do get 'movies/index' get 'movies/show' get 'movies/new' get 'movies/create' get 'movies/edit' get 'movies/update' get 'movies/destroy' root 'pages#home' devise_for :users #get 'pages/home' # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html end
We’ll get rid of all these
get requests and instead just do resources, like this:
Rails.application.routes.draw do # get 'movies/index' # get 'movies/show' # get 'movies/new' # get 'movies/create' # get 'movies/edit' # get 'movies/update' # get 'movies/destroy' root 'pages#home' resources :movies devise_for :users # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html end
Alright, so let’s take our detour and use the rails console first.
Add a New Movie With Rails Console
To add a movie, we first need to make sure we have a user in the database. Let’s run
User.second in the console.
As we can see, we have the first User in the database, but for the second, it returns
Let’s now see if there are any movies that belong to
Finally, let’s add one.
We’ll need to add all the entries for each of the
movies table columns, except for the first table column,
As a reminder, here’s what gets returned from running
Ok, so now we’re ready to add a movie.
I’ve split the following code on several lines for easier reading; however, you should type it as a one-liner in the Rails console:
User.first.movies.new( title:"The Matrix", description:"Neo Anderson lives in a simulation...", img:"https://upload.wikimedia.org/wikipedia/en/thumb/c/c1/The_Matrix_Poster.jpg/220px-The_Matrix_Poster.jpg", cover:"https://upload.wikimedia.org/wikipedia/en/thumb/c/c1/The_Matrix_Poster.jpg/220px-The_Matrix_Poster.jpg").save
Here’s the output in the console:
Let’s now run
Here’s the output this time:
Now that we have added our first movie using the Rails console, let’s add the views so that our web app visitors can also do it from our web app’s frontend.
Adding the Movie CRUD Views
Let’s start with movies edit. Open
app/views/movies/edit.html.erb and add this code at the bottom:
<%= form_form :movie, url: movie_path, method :patch do |f| %> Title: <%= f.text_field :title %><br> Description: <%= f.text_field :description %><br> <%= f.submit %> <% end %>
Now, visit this URL:
You’ll get an error.
Why is that?
To understand why this is happenning, let’s visit a different URL:
This time, the actual URL gets served without issues:
The reason is the code in the
While we do have the code for adding a new movie in the controller, we don’t have a definition for the
def new @movie = Movie.new puts @movie end . . def edit end
Note the the two vertical dots in the code above are added for brevity, and are a stand-in for the actual code.
Obviously, we need to add some code to our
Here it is:
def edit @movie = Movie.find(params[:id]) end
To be continued…