Ruby on Rails:无法从注释访问用户属性



在这个应用程序中,我有一个配方模型,用户模型(Devise(和一个注释模型。

class User < ActiveRecord::Base
  has_many :recipes
  has_many :comments
end
class Recipe < ActiveRecord::Base
  belongs_to :user
  has_many :comments, :dependent => :destroy
end
class Comment < ActiveRecord::Base
  belongs_to :user
  belongs_to :recipe
end

这就是我的注释控制器的样子。

class CommentsController < ApplicationController
  def create
    @recipe = Recipe.find(params[:recipe_id])
    @comment = @recipe.comments.build(comment_params)
    @comment.user = current_user
    if @comment.save
      redirect_to root_path #change this the the appropriate path later
    end
  end
  private
  def comment_params
    params.require(:comment).permit(:content)
  end
end

这是我收到的错误

ActionView::Template::Error (undefined method `first_name' for nil:NilClass):
68:     <p><%= @recipe.comments.count %> Comments</p>
69:     <% @recipe.comments.each do |comment| %>
70:       <p><%= comment.content %><p>
71:       <%= comment.user.first_name %>
72:     <% end %>
73:   </div>
74: </div>

因此,当我尝试访问 first_name 属性时出现问题。如果我只是"评论用户",它会吐出#用户:0x007f8f4f9b6780。

我查看了 Rails 控制台,发现我的评论正在保存。在 Rails 控制台中,我能够做到

@recipe = Recipe.first
@recipe.comments.first.user.first_name => "John"

这是失败的实际代码

<% @recipe.comments.each do |comment| %>
  <% byebug %>
  <p><%= comment.content %><p>
  <p><%= comment.user.first_name %></p>
<% end %>

我尝试使用 byebug 进行调试,我能够做"comment.user.first_name"=>约翰

不确定我在这里有什么问题,并希望得到帮助。轨道 4.2.0 顺便说一句

编辑:食谱控制器#显示

class RecipesController < ApplicationController
  before_action :find_user, only: [:edit, :update, :show, :destroy]
  before_action :find_recipe, only: [:edit, :update, :show, :destroy]

  ...
  def show
  end
  private
  def find_user
    @user = User.find(params[:user_id])
  end
  def find_recipe
    @recipe = Recipe.find(params[:id])
  end
end

评论表部分

<div class="container">
  <% if user_signed_in? %>
  <%= form_for([@recipe.user, @recipe, @recipe.comments.build]) do |f| %>
    <%= f.label :content %><br>
    <%= f.text_area :content %><br>
    <br>
    <%= f.submit class: "btn btn-default" %>
  <% end %>
<% end %>
</div>

因为您正在迭代注释集合

@recipe.comments.each

您提到的错误发生,因为其中一个注释没有设置user(这会导致first_name调用nil,以及您提到的错误(。

尝试按如下方式修改模板,以跟踪"有问题"的注释:

<% @recipe.comments.each do |comment| %>
  <p><%= comment.content %><p>
  <% if comment.user.nil? %>
    Anonymous
  <% else %>
    <%= comment.user.first_name %>
  <% end %>
<% end %>

希望对您有所帮助!

更新

尝试更新RecipesController

class RecipesController < ApplicationController
  def show
    @comment = Comment.new
  end
end

并将@recipe.comments.build部分替换为:

<div class="container">
  <% if user_signed_in? %>
    <%= form_for([@recipe.user, @recipe, @comment]) do |f| %>
      <%= f.label :content %><br>
      <%= f.text_area :content %><br>
      <br>
      <%= f.submit class: "btn btn-default" %>
    <% end %>
  <% end %>
</div>

此时您不需要"链接"@recipeComment,因为它将在CommentsController#create中得到正确处理。

那是很好的锻炼!

从技术上讲,您从不检查current_user是否存在。因此,当您迭代每个评论时,如果未登录的某人发布评论,则用户将为零。

为了防止此错误,您可以在控制器中添加此设计助手:

class CommentsController < ApplicationController
  before_filter :authenticate_pro_user!

如果有人尝试访问评论控制器,它将自动重定向到登录视图。

处理这个问题的更好方法是使用委托并允许 nil,让我解释一下

class Comment < ActiveRecord::Base
  belongs_to :user
  belongs_to :recipe
  delegate :first_name, to: :user, prefix: true, allow_nil: true
end

这将创建一个名为 user_first_name 的方法(userprefix (,allow_nil表示如果用户不存在,则返回nil

视图代码将是这样的

<% @recipe.comments.each do |comment| %>
  <p><%= comment.content %><p>
  <p><%= comment.user_first_name %></p> # notice it's 1 dot
<% end %>

当循环到达没有用户的注释时,它将返回nil这将变成一个空字符串,
而当用户存在时,评论将要求用户返回其first_name

最新更新