Instance variable can't be assigned via privat

2019-09-01 10:08发布

问题:

When I use this link:

<%= link_to "upvote", post_upvote_path(post), method: :put %>

I get error :

undefined method `liked_by' for nil:NilClass Which is caused because variable @post inside method upvote is not properly assigned.

  def upvote
    @post = current_post
    @post.liked_by current_user
    redirect_to @post
  end

private
    def current_post
      current_user.posts.find_by(id: params[:id])
    end

private method current_post works fine in other methods inside this controller. However, inside this method it doesn't. For example If I used:

  def upvote
    @post = Post.first
    @post.liked_by current_user
    redirect_to @post
  end

instead, it would work fine, except for the part it would upvote first post instead of the one where link is clicked. What is the right approach to this problem? How do I assign this variable properly to work for post where upvote link is clicked?

The rake routes | grep posts output:

I noticed that this method has /posts/:post_id ... while others use :id . That might be the issue, how do I change it?

回答1:

In your link_to you're passing post...is it in the context of an @posts loop? What page are you on when the voting is being done?

Try this:

def upvote
  puts "params: #{params.inspect}"
  @post = Post.find(params[:id]) # if this isn't working check out that puts statement in the stack trace
  @post.liked_by current_user 
  redirect_to post_path(@post)
end


回答2:

in your routes file there is

post_upvote /posts/:post_id/upvote and not post_upvote /posts/:id/upvote

so actually when you pass post to post_upvote_path method like that post_upvote_path( post) in your controller you want to do params[:post_id] and not params[:id] because of what you wrote in your routes file

def current_post
  current_user.posts.find_by(id: params[:post_id]) #post_upvote /posts/:post_id/upvote
end


回答3:

I found where was the problem. In routes file I needed to nest member method like this:

  resources :posts do
    member do
      put 'upvote', to: 'posts#upvote'
    end
  end

And then change view to this:

  <%= link_to "upvote", upvote_post_path(post), method: :put %>

And this method in controller works just right:

  def upvote
    @post = Post.find(params[:id])
    @post.liked_by current_user
  end


回答4:

This:

<%= link_to "upvote", post_upvote_path(post), method: :put %>

Should become:

<%= link_to "upvote", post_upvote_path(@post), method: :put %>