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

5
  • Did you setup the relationship on the User model? has_many :playlists Commented Nov 9, 2014 at 1:03
  • Also, you may want to use @user.playlists.build. Notice the .build, which will automatically set the associated foreign key. Commented Nov 9, 2014 at 1:05
  • I do already have the models setup accordingly, thanks. Where would I use the .build? Commented Nov 9, 2014 at 1: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 Commented Nov 9, 2014 at 1: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. Commented Nov 9, 2014 at 1:14

1 Answer 1

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

Sign up to request clarification or add additional context in comments.

2 Comments

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?
You can find some information here: guides.rubyonrails.org/…

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.