user3760731
user3760731

Reputation:

Ruby on Rails - Associations, Models and Favorites

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

Answers (2)

Richard Peck
Richard Peck

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

enter image description here

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

Pavan
Pavan

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

Related Questions