Reputation:
I am creating an application involving one of my passions, movies. I would like my users to be able to select their favorite movies. When running the code from my project, I received the following error message.
Undefined Method: 'favorites' for nil:NilClass
<h1>My Favorites</h1>
<%= @favorites = @user.favorites.to_a %>
<table class = "favorites-table">
<thead>
I was testing out favorites functionality through my work in the console, so I played around with the above code in my favorites.html.erb file.
The only problem with that is I set up the associations and proved I could access that method through rails console.
u = User.first
u.favorites => nil
u.favorites.to_a << Movie.first
This worked in ActiveRecord but not in rails server.
Here are my associations (Movie, User and Favorite)
class Movie < ActiveRecord::Base
belongs_to :user
has_many :favorite_movies
has_many :favorited_by, through: :favorite_movies, source: :user
end
class User < ActiveRecord::Base
has_many :favorites, foreign_key: "user_id", dependent: :destroy
has_many :favorite_movies, through: :favorites, source: :movies
end
class Favorite < ActiveRecord::Base
belongs_to :user
validates :movie_id, presence: true
validates :user_id, presence: true
end
I created these associations through reading the Rails Tutorial chapter 11, but I'm not sure if it works for favorites, and I may have made a couple mistakes.
Here are my important migrations (Movies, Users, Favorites)
class CreateMovies < ActiveRecord::Migration
def change
create_table :movies do |t|
t.string :title
t.string :director
t.string :genre
t.integer :movie_id # Potential land-mine
t.integer :release_date
t.integer :reviewed
t.integer :stars
t.timestamps
end
end
end
My User Migration file
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.integer :user_id # Potential land-mine. Users already have separate ID column (primary key)
t.string :name
t.string :password
t.string :email
t.timestamps
end
end
end
class CreateFavorites < ActiveRecord::Migration
def change
create_table :favorites do |t|
t.integer :user_id
t.integer :movie_id
t.timestamps
end
add_index :favorites, :user_id
add_index :favorites, :movie_id
add_index :favorites, [:user_id, :movie_id], unique: true
end
end
Here are my routes
resources :movies do
put :favorite, on: :member
end
resources :users
I have a Favorites method defined in my Users controller but I have nothing inside it. Here is my controller
class UsersController < ApplicationController
before_action :signed_in_user, only: [:edit, :update, :destroy]
before_action :correct_user, only: [:edit, :update]
def index
@users = User.all
end
def show
@user = User.find(params[:id])
end
def new
@user = User.new
end
def create
@user = User.new(user_params)
if @user.save
flash[:success] = "Congratulations #{@user.name}! You have successfully created an account"
redirect_to @user
else
render 'new'
end
end
def update
end
def destroy
end
def favorites
end
private
def user_params
params.require(:user).permit(:name, :email, :password, :password_confirmation) # Potential Land-mine
end
def signed_in_user
unless signed_in?
store_location
redirect_to signin_url notice: "Please sign in."
end
end
def correct_user
@user = User.find(params[:id])
redirect_to(root_url) unless current_user?(@user)
end
end
I think the problem might lie with my use of ids. I have two ids for both users and movies (user_id, id, movie_id, id).
I have read various documentation, some similar questions on Stack Overflow and the Rails Tutorial to come up with my code, but I'm really confused. I'm not sure what I'm doing wrong, and I would like to better understand associations.
Any help is greatly appreciated.
Upvotes: 1
Views: 1258
Reputation: 76784
If you're going to use a has_many :through
, you'll need to include both models in a belongs_to
relationship in your join model:
#app/models/favorite.rb
Class Favorite < ActiveRecord::Base
belongs_to :user
belongs_to :movie
end
You may also wish to change your reference to :favorite_movies
to just :movies
:
#app/models/user.rb
Class User < ActiveRecord::Base
has_many :favorites
has_many :movies, through: :favorites
end
#app/models/movie.rb
Class Movie < ActiveRecord::Base
has_many :favorites
has_many :users, through: :favorites
end
I've not read the tutorial you've been using, so I can't comment on your current setup; however, what I've proposed is the most rudimentary "Rails" way to do it.
Hope this helps further!
Upvotes: 3
Reputation: 33542
Your @user
value is nil because you haven't defined it in your controller for the related method.
Fix
In the controller,add this line @user = User.find(params[:id])
to favorites
method as below
def favorites
@user = User.find(params[:id])
end
Upvotes: 1