首页 文章

NoMethodError:未定义的方法''为nil:NilClass

提问于
浏览
0

我有这些型号和控制器代码:

class Item < ActiveRecord::Base
 has_many :receivers_collateral_items, through: :received_trades, source: :wanted_item
 has_many :receivers_wanted_items, through: :received_trades, source: :collateral_item
 has_many :requesters_collateral_items, through: :requested_trades, source: :collateral_item
 has_many :requesters_wanted_items, through: :requested_trades, source: :wanted_item
 validates :year, :price, presence: true, numericality: true
 validates :shares, numericality: 
                     { only_integer: true, greater_than_or_equal_to: 0, 
                     less_than_or_equal_to: :build_shares }

 accepts_nested_attributes_for :receivers_collateral_items, :receivers_wanted_items, 
                               :requesters_collateral_items, :requesters_wanted_items, 
                               allow_destroy: true
end




class Trade < ActiveRecord::Base
 belongs_to :trade_requester, class_name: "User"
 belongs_to :trade_recipient, class_name: "User"
 belongs_to :wanted_item, class_name: "Item"
 belongs_to :collateral_item, class_name: "Item"
 validates :trade_requester, :trade_recipient, :wanted_item, :collateral_item, presence: true
 validates :shares, numericality: 
                     { only_integer: true, greater_than_or_equal_to: 0, 
                     less_than_or_equal_to: :max_shares }

 accepts_nested_attributes_for :wanted_item, :collateral_item, allow_destroy: true

 def max_shares
    if wanted_item.shares > collateral_item.shares
        collateral_item.shares
    else
        wanted_item.shares
    end
 end
end


class TradesController < ApplicationController
 def create
  @trade = current_user.requested_trades.build(trade_params)
 end

 private
  def trade_params
   params.require(:trade).permit(:trade_requester_id, :trade_recipient_id, :wanted_item_id, :collateral_item_id, :shares)
  end
end

但是我得到了 NoMethodError: undefined method 'shares' for nil:NilClass 和一个bd回滚 . 重现错误的步骤:

t = Trade.new
t.trade_requester_id = User.find(1)
t.trade_recipient_id = User.find(2)
t.wanted_item_id = Item.second
t.collateral_item_id = Item.first
t.shares = 100
t.save

堆栈跟踪表明它来自 Trade 中的 numericality 验证,但在我看来,正在调用的类 shares 应该存在 . 我应该能够调用 t.wanted_item 并获得该项目,但我得到 nil . 但是, t.wanted_item_id 会返回该ID . 为什么是这样?

1 回答

  • 2

    这可能是因为新记录缺少 wanted_itemcollateral_item . 如果这两个关联中的任何一个返回 nil ,那么将抛出异常 . 要解决此问题,请使用旧版本的Ruby try 或Ruby 2.3或更高版本的安全导航操作符( &. ) .

    这应该解决你的问题(Ruby 2.3之前):

    def max_shares
      if wanted_item.try(:shares).to_i > collateral_item.try(:shares).to_i
        wanted_item.shares
      else
        collateral_item.try(:shares).to_i
      end
    end
    

    try 方法将返回 wanted_item.sharescollateral_item.shares 的值(如果它们不是 nil ) . 如果其中一个是 nil ,那么 try 将捕获异常并返回 nil . to_inil 转换为零 .

    因此,如果 collateral_item 存在, collateral_item.try(:shares).to_i 将返回 shares 的值,如果 collateral_itemnil ,它将返回零 .

    如果您使用的是Ruby 2.3或更高版本,则应将 _item.try(:shares).to_i 替换为 _item&.shares.to_i . 后者看起来更干净,faster in execution .

    Update

    对于复制,请确保将记录分配给关联而不是其ID:

    t = Trade.new
    t.trade_requester = User.find(1)
    t.trade_recipient = User.find(2)
    t.wanted_item = Item.second
    t.collateral_item = Item.first
    t.shares = 100
    t.save!
    

相关问题