【问题标题】:NoMethodError: undefined method '' for nil:NilClassNoMethodError: nil:NilClass 的未定义方法''
【发布时间】:2017-10-20 01:17:58
【问题描述】:

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

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。这是为什么呢?

【问题讨论】:

  • 提供将错误重复为代码而不是描述的步骤。
  • @max t = Trade.new,t.trade_requester_id = 1,t.trade_recipient_id = 2,t.wanted_item_id = 2,t.collateral_item_id = 1,t.shares = 100,然后是t.save,这实际上引发了无方法错误
  • 将其添加到问题的正文中 - 不是评论。
  • 只是为了删除另一个变量,尝试注释掉accepts_nested_attributes_for。
  • 您可以尝试在max_shares 中的collatral_item 和wanted_item 前面添加self.。它不应该有所作为,但值得一试。我在这里抓住了稻草。

标签: ruby-on-rails ruby


【解决方案1】:

这可能是因为新记录确实缺少wanted_item 或collateral_item。如果这两个关联中的任何一个返回nil,则会引发异常。要解决此问题,请使用 try 处理旧版本的 Ruby,或使用安全导航运算符 (&amp;.) 处理 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.shares 和 collateral_item.shares 的值,如果它们不是 nil。如果其中之一是nil,那么try 将捕获异常并返回nil。 to_i 将 nil 转换为零。

所以如果collateral_item 存在,collateral_item.try(:shares).to_i 将返回shares 的值,如果collateral_item 是nil,它将返回零。

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

更新

对于复制,请确保您将记录分配给您的关联而不是它们的 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!

【讨论】:

  • 使用try 的唯一目的是抑制堆栈跟踪有什么意义? Trade 不会保存到数据库,除非一切都经过验证。我想弄清楚的是为什么那些Items是nil。
  • 好问题。这可能是因为具有这些 ID 的用户不存在。 t.wanted_item 和 t.collateral_item 在 t.save 之前返回什么?
  • 当我用t检查我试图保存的记录时,我得到#&lt;Trade id: nil, trade_requester_id: 1, trade_recipient_id: 2, wanted_item_id: 2, collateral_item_id: 1, approved: nil, shares: 100&gt;。这是预期的。但是当我检查t.wanted_item_id 或t.collateral_item_id 时,我得到nil。这是出乎意料的
  • 尝试以这种方式设置您的关联:t.trade_requester = User.find(1) 并确保 User.find(1) 返回一条记录。
  • t.collateral_item 和 t.wanted_item 是否返回 nil 以外的其他内容?另外,Item.first 和Item.second 是否返回非nil 值?最后,确保id 不是作业的一部分。示例:分配应该是t.collateral_item = Item.first
猜你喜欢
  • 2013-11-14
  • 2013-01-14
  • 2017-10-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多