【问题标题】:Nested Comments with nested answers: undefined method `answers' for nil:NilClass | Rails带有嵌套答案的嵌套评论:nil 的未定义方法“答案”:NilClass |导轨
【发布时间】:2015-09-19 08:11:40
【问题描述】:

我正在尝试在 cmets 中实现嵌套答案,这些答案嵌套在拍卖中。

有一个auctions.rb 模型,它:

has_many :comments, dependent: :destroy
has_many :answers, :through => :comments

一个 cmets.rb 模型,其中:

belongs_to :auction
has_many :answers, dependent: :destroy

answers.rb 模型,其中:

belongs_to :comment

answers_controller 继承自 cmets_controller:

class AnswersController < CommentsController
  before_action :all_answers, only: [:index, :create, :update, :destroy]
  before_action :set_answer, only: [:edit, :update, :destroy]
  respond_to :html, :js

  # New Answer (Form)
  def new
    @answer = Answer.new
    @comments.answers.build
  end

  # Create Answer
  def create
    @answer = @comment.answers.build(answer_params)
    @answer.user_id = current_user.id
    @answer.save
  end

  # Edit Answer
  def update
    @answer.update!(answer_params)
  end

  # Delete Answer
  def destroy
    @answer = Comment.find(params[:id])
    @comment = @answer.comment
    @answer.destroy
  end

  private

  def all_answers
    @answers = @comment.answers.all
  end

  def set_answer
    @answer = @comment.answers.find(params[:id])
  end

  def answer_params
    params.require(:comment).permit(:body)
  end
end

错误:

拍卖中的 NoMethodError#show app/views/cmets/_comment.html.erb 其中第 20 行提出:nil:NilClass 的未定义方法“答案”

     <div class="col s12" id="answer-form" style="display:none;"></div>
   </div>
   <div class="row">
     <div class="col s12" id="answers"><%= render @comment.answers %></div>
   </div>

使用 我想在相关评论下方显示所有现有答案。我做错了什么?

auction_controller

class AuctionsController < ApplicationController
  # Index of all auctions
  def index
    @auctions = Auction.all
  end

  # Show Auction by :id
  def show
    @auction = Auction.find(params[:id])
    # Find Seller by ID
    @seller = User.find(@auction.user_id)
    # Find highest bid, by finding all related bids and ordering in descending and picking the first
    @highest_bid = Bid.where(auction_id: params[:id]).order("amount DESC").first
    # Find product
    @product = Product.find(@auction.product_id)
  end

  # New Auction Form
  def new
    @auction = Auction.new
  end

  # Edit Auction
  def edit
    @auction = Auction.find(params[:id])
  end

  # Create new Auction
  def create
    # Create new Auction
    @auction = Auction.new(auction_params)
    # Save Id of User (Seller)
    @auction.user_id = current_user.id
    # If auction was created successfully
    if @auction.save
      # display the created auction
      redirect_to @auction, :notice => "Auction created"
    else
      # display Form again if unsuccessful
      render 'new'
    end
  end

  # Update existing Auction
  def update
    @auction = Auction.find(params[:id])
    # Validation
    if @auction.update(auction_params)
      redirect_to @auction, :notice => "Auction updated"
    else
      render 'edit'
    end
  end

  # Delete Auction
  def destroy
    @auction = Auction.find(params[:id])
    @auction.destroy

    redirect_to auctions_path, :notice => "Auction deleted"
  end

  private
  # set required parameters for new created Auctions
  def auction_params
    params.require(:auction).permit(:condition, :product_name)
  end
end

cmets_controller

class CommentsController < ApplicationController
  before_action :set_auction
  before_action :all_comments, only: [:index, :create, :update, :destroy]
  before_action :set_comment, only: [:edit, :update, :destroy]
  respond_to :html, :js

  # New Comment (Form)
  def new
    @comment = Comment.new
    @auction.comments.build
  end

  # Create Comment
  def create
    @comment = @auction.comments.build(comment_params)
    @comment.user_id = current_user.id
    @comment.save
  end

  # Edit Comment
  def update
    @comment.update!(comment_params)
  end

  # Delete Comment
  def destroy
    @comment = Comment.find(params[:id])
    @auction = @comment.auction
    @comment.destroy
  end

  private
  def set_auction
    @auction = Auction.find(params[:auction_id])
  end

  def all_comments
    @comments = @auction.comments.all
  end

  def set_comment
    @comment = @auction.comments.find(params[:id])
  end

  def comment_params
    params.require(:comment).permit(:body)
  end
end

正常的评论工作。只有评论答案不起作用。

【问题讨论】:

  • 引发错误的操作是什么?新的?指数?等等?
  • 动作是:Auctions#show,我们可以看看你的拍卖控制器吗?看起来@comment 没有定义。
  • @SimoneCarletti - 在拍卖的显示操作中出现错误。
  • @charly-m - 我添加了拍卖控制器和 cmets 控制器。评论的定义应该没有问题(只有答案不起作用)
  • 我认为您忘记在 Auction#show 中定义 @comment ,或者您没有使用正确的路线。

标签: ruby-on-rails ruby-on-rails-4


【解决方案1】:

错误发生在Auctions#show,错误清楚地告诉你你试图在一个nil对象上调用answers。因此,这意味着@comment 在该视图中为零。

事实上,如果您检查show 操作,您永远不会获取/分配任何对象到@comment

  # Show Auction by :id
  def show
    @auction = Auction.find(params[:id])
    # Find Seller by ID
    @seller = User.find(@auction.user_id)
    # Find highest bid, by finding all related bids and ordering in descending and picking the first
    @highest_bid = Bid.where(auction_id: params[:id]).order("amount DESC").first
    # Find product
    @product = Product.find(@auction.product_id)
  end

为了修复它,请确保 @comment 已正确分配给 Comment 实例。

【讨论】:

    【解决方案2】:

    这里还有一个问题:

      def new
        @answer = Answer.new
        @comments.answers.build
      end
    

    您没有一个名为@comments 的变量,因此您的表单实际上无法从中构建答案。事实上,你在其他两种方法中调用@comment,我什至看不到它被声明:

      def all_answers
        @answers = @comment.answers.all
      end
    
      def set_answer
        @answer = @comment.answers.find(params[:id])
      end
    

    您声明@comment的唯一一次是在destroy方法中:

      def destroy
        @answer = Comment.find(params[:id])
        @comment = @answer.comment
        @answer.destroy
      end
    

    即便如此,还是很奇怪。

    为什么要使用 @answer 变量调用 Comment 模型?你肯定有一个Answer 模型和comments 通过has_many 关系附加?

    我建议你保持简单:

    #app/models/answer.rb
    class Answer < ActiveRecord::Base
       has_many :comments
    end
    
    #app/models/comment.rb
    class Comment < ActiveRecord::Base
       belongs_to :answer
    end
    

    这意味着当您调用您的操作控制器时,您将能够应用以下内容:

    def show
       @answer = Answer.find params[:id]
       @comment = @answer.comments.build
    end
    

    如果您想让 cmets 作为答案,您需要将模型分开。使用层次结构的 gem,例如 closure tree。这样,您将能够保持您的答案/评论的层次顺序,同时保持模型一致。

    【讨论】:

    • 感谢您的回答。我不得不提一下,我两周前开始学习 Rails。首先,我只有拍卖和评论模型。正常的 cmets 工作正常(评论就像一个问题顺便说一句)。然后我想,将答案实施到 cmets 中会很容易(就像我将 cmets 实施到拍卖中一样)。因此我做了这样的路线:resources :auctions do resources :comments do resources :answers end end 我试图做的解决方案完全不可能吗?
    • 不错!你以前有过编程经验吗?在开始学习的 2 周内尝试似乎相当复杂!
    • 我只知道html、css、一点点js、java和cake/php。到目前为止,我更多地关注前端。但自从我意识到如果我可以做后端开发有什么可能,我立即开始学习 Ruby 基础和 Rails。是的,这对我来说非常复杂,但我认为学习某件事的最好方法是实践并从失败中学习。
    • 是的,你完全正确 :) 继续前进,你的想法是对的。您过去的编程经验也会对您有很大帮助!
    • 谢谢你的好话 :)
    猜你喜欢
    • 2021-11-24
    • 1970-01-01
    • 2015-04-17
    • 2016-11-14
    • 2019-10-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-25
    相关资源
    最近更新 更多