为什么在明显存在的对象上得到“ nil:NilClass的未定义方法”?

时间:2018-10-31 19:29:09

标签: ruby ruby-on-rails-5 rails-activerecord

控制器

def show
  @dailies = Daily.where(store_id: params[:store]).order(created_at: :asc).by_month_year(params[:month],params[:year])
  @services = Service.all.order(id: :asc)
end

视图

<%= daily_data_for(params[:store], params[:month], date.day, params[:year], 'starting_safe') %>

助手

def daily_data_for(store, month, day, year, field)
  Daily.where(store_id: store, month: month, day: day, year: year).first().send(field)
end

尝试在视图中显示ActiveRecord对象属性时出现错误undefined method 'starting_safe' for nil:NilClass。我有点困惑,因为它不应该是nil,因为如果我从助手中删除.send(field),我会得到#<Daily:0x00007fea597aad50>。因此它正在返回一个对象。因此,然后在视图中检查Activerecord对象:

daily_data_for(params[:store], params[:month], date.day, params[:year], 'starting_safe').inspect

哪个显示#<Daily id: 1030, store_id: 1, created_at: "2018-10-01 18:20:07", updated_at: "2018-10-01 18:20:07", starting_safe: 0.1e1, ending_safe: 0.1e1, total_in: 0.1e1, total_out: 0.1e1, sum: 0.1e1, starting_drawer: 0.1e1, ending_drawer: 0.1e1, over_short: 0.1e1, year: "2018", month: "10", day: "1">

它正在获取正确的记录并返回它,但是当我尝试访问starting_safe时,我得到了nil类的未定义方法。

为什么我不能在视图中显示starting_safe?我想在帮助器中使用实例变量@dailies,而不是再次查询数据库,但是我遇到了相同的错误。我已经在控制台中尝试过,并且一切正常。我对这里发生的事情有些迷惑。

2 个答案:

答案 0 :(得分:2)

我非常确定您检查的记录(每日ID为1030)和失败的[不存在]记录是两个不同的记录。这是您如何找到的。暂时修改这样的方法:

def daily_data_for(store, month, day, year, field)
  daily = Daily.where(store_id: store, month: month, day: day, year: year).first
  raise "Daily not found for #{store.inspect}, #{month.inspect}, #{day.inspect}, #{year.inspect}" unless daily
  daily.send(field)
end

现在,错误消息应该更加明显。

答案 1 :(得分:0)

错误undefined method 'starting_safe' for nil:NilClass专门告诉您,您无法在starting_safe上呼叫nil。由于您的助手方法调用了范围.where方法,因此在其上调用.first并不能确保它不会是一个空的activerecord关系对象。如果为空,则在其上调用.first将返回nil。因此,您可能只想使用safe navigation是安全的。

def daily_data_for(store, month, day, year, field)
  Daily.where(store_id: store, month: month, day: day, year: year).first&.send(field)
end

或者,如果您碰巧是<2.3的Ruby版本,请使用.try

Daily.where(store_id: store, month: month, day: day, year: year).first.try(:send, field)
#try will only run if first is not nil