2

I'm trying to make a simple rails app where users can create any number of playlists. I am very new to rails and am wondering if I should be nesting the resources like this:

Rails.application.routes.draw do
  resources :users do
    resources :playlists
  end

Doing it that way yields this when I rake routes:

            Prefix Verb   URI Pattern                                  Controller#Action
    user_playlists GET    /users/:user_id/playlists(.:format)          playlists#index
                   POST   /users/:user_id/playlists(.:format)          playlists#create
 new_user_playlist GET    /users/:user_id/playlists/new(.:format)      playlists#new
edit_user_playlist GET    /users/:user_id/playlists/:id/edit(.:format) playlists#edit
     user_playlist GET    /users/:user_id/playlists/:id(.:format)      playlists#show
                   PATCH  /users/:user_id/playlists/:id(.:format)      playlists#update
                   PUT    /users/:user_id/playlists/:id(.:format)      playlists#update
                   DELETE /users/:user_id/playlists/:id(.:format)      playlists#destroy
             users GET    /users(.:format)                             users#index
                   POST   /users(.:format)                             users#create
          new_user GET    /users/new(.:format)                         users#new
         edit_user GET    /users/:id/edit(.:format)                    users#edit
              user GET    /users/:id(.:format)                         users#show
                   PATCH  /users/:id(.:format)                         users#update
                   PUT    /users/:id(.:format)                         users#update
                   DELETE /users/:id(.:format)                         users#destroy
              root GET    /                                            default_pages#home
            signup GET    /signup(.:format)                            users#new
            signin GET    /signin(.:format)                            users#signin

I can successfully get to /users/1/playlists in which I have this link (inside index.html.erb of playlists):

<%= link_to 'New Playlist', new_user_playlist_path(params[:user_id])%>

But clicking it gives me this error:

     undefined method `playlists' for nil:NilClass

   Extracted source (around line #18):
   16       # GET /playlists/new
   17       def new
   18         @playlist = @user.playlists.new
   19       end
   20       # GET /playlists/1/edit

This is what my playlist_controller looks like:

class PlaylistsController < ApplicationController
  before_action :set_playlist, only: [:show, :edit, :update, :destroy]
                :set_user

  # GET /playlists
  # GET /playlists.json
  def index
    @playlists = Playlist.all
  end

  # GET /playlists/1
  # GET /playlists/1.json
  def show
  end

  # GET /playlists/new
  def new
    @playlist = @user.playlists.new
  end

  # GET /playlists/1/edit
  def edit
  end

  # POST /playlists
  # POST /playlists.json
  def create
    @playlist = @user.playlists.new(playlist_params)

    respond_to do |format|
      if @playlist.save
        format.html { redirect_to @user.playlist, notice: 'Playlist was successfully created.' }
        format.json { render :show, status: :created, location: @playlist }
      else
        format.html { render :new }
        #format.json { render json: @playlist.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /playlists/1
  # PATCH/PUT /playlists/1.json
  def update
    respond_to do |format|
      if @playlist.update(playlist_params)
        format.html { redirect_to @playlist, notice: 'Playlist was successfully updated.' }
        format.json { render :show, status: :ok, location: @playlist }
      else
        format.html { render :edit }
        format.json { render json: @playlist.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /playlists/1
  # DELETE /playlists/1.json
  def destroy
    @playlist.destroy
    respond_to do |format|
      format.html { redirect_to playlists_url, notice: 'Playlist was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

  private
    def set_user
      @user = User.find_by(params[:user_id])
    end
    # Use callbacks to share common setup or constraints between actions.
    def set_playlist
      @playlist = Playlist.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def playlist_params
      params.require(:playlist).permit(:user_id, :title, :img)
    end
end

My thought is that since the expected URL is supposed to look something like users/1/playlists/1, that I need to pass two variables, one for the user_id and one for a playlist id (of some sort) but I don't know exactly where/how...Can anyone help me out?

Thanks

user2804881
  • 107
  • 8
  • Did you setup the relationship on the `User` model? `has_many :playlists` – dasnixon Nov 09 '14 at 01:03
  • Also, you may want to use `@user.playlists.build`. Notice the `.build`, which will automatically set the associated foreign key. – dasnixon Nov 09 '14 at 01:05
  • I do already have the models setup accordingly, thanks. Where would I use the .build? – user2804881 Nov 09 '14 at 01:07
  • When you set the `@playlist` under your `new` action. `@playlist = @user.playlists.build`. Also, I don't think you want `Playlist.all` under `index`. Since playlist is associated with a `User`, probably should be `@playlists = @user.playlists` – dasnixon Nov 09 '14 at 01:09
  • That makes a lot of sense, thanks! I haven't caught that yet since I'm just copying a tutorial and fixing errors as I go to learn. – user2804881 Nov 09 '14 at 01:14

1 Answers1

3

I think the problem is in before_action, that's why @user is not set. Try to add one more time before_action under line before_action :set_playlist, only: [:show, :edit, :update, :destroy]

class PlaylistsController < ApplicationController
  before_action :set_playlist, only: [:show, :edit, :update, :destroy]
  before_action :set_user, only: [:new, :create]

  ...
end

UPD

I've found one more place. Try to use @user inside your view. new_user_playlist_path(@user), it should be already instantiated from before_action

Bob
  • 2,081
  • 18
  • 25
  • At first I got this error: ActiveRecord::InverseOfAssociationNotFoundError in PlaylistsController#new Could not find the inverse association for user (:playlist in User) And I remembered that I had this in my Playlist model: class Playlist < ActiveRecord::Base belongs_to :user, inverse_of: :playlist validates :user_id, presence: true end But I never really understood why I needed the inverse_of: part. After removing ", inverse_of: :playlist" I tried again and it worked! Do you know why I would need the inverse_of part? What purpose does it serve? – user2804881 Nov 09 '14 at 01:12
  • You can find some information here: http://guides.rubyonrails.org/association_basics.html#bi-directional-associations – Bob Nov 09 '14 at 01:15