2014-05-15 42 views
2

我试图更新对象的属性,但通常我尝试更新的对象不再存在。update_attributes()与update_all()&on已删除的对象

例如为:我是后处理的CSV文件,以获得属性:

array.each do |a| 
    player = Player.find_by_grepo_id(a[:grepo_id]) 
    player.update_attributes(a) 
end 

时未找到播放器将抛出一个错误。

我从以往的经验教训:

的ActiveRecord :: Base.find总是抛出异常,如果它没有找到一个纪录,这是故意的。 我应该只使用查找,如果我绝对期望有任何我正在寻找。 如果我正在展示一个show动作并找不到该文章,我应该挽救该异常并呈现404(未找到) 而不是重定向到索引(技术上)。

如果我想通过它的id属性找到某些东西而不强制异常,我应该使用动态查找器 find_by_id(在我的情况下为find_by_grepo_id)如果它没有找到具有该id的记录,将返回false。

但是一旦运行包含上述代码的任务,我得到

NoMethodError: undefined method `update_attributes' for nil:NilClass 

这是因为与该特定ID的玩家已经不存在了。如果我使用.present?方法将update_attributes调用包装起来。

我在想什么?不应该find_by_id方法不抛出一个错误,只是跳过它?

+1

当您尝试更新时发生错误,而不是查找。所以它不会在'Player.find_by_grepo_id(a [:grepo_id])上抛出一个错误' –

+0

你的错误正在清除告诉,什么是混乱..再次阅读错误。 –

+0

更新调用是否有类似的方式,还是必须将它包装在.present中?方法? –

回答

3

如果你想这样做在一个电话,而不是两个,你可以使用update_all方法是这样的:

Player.where(:grepo_id => a[:grepo_id]).update_all(a) 

这将导致以下SQL:

UPDATE players SET ... = ..., ... = ... WHERE players.grepo_id = ... 

如果grepo_id不存在,也可以使用:没有任何更新。但是请注意,这只是运行SQL; 您的模型的任何验证或回调都将被忽略

+0

Humm ..很好学习。我对铁轨很陌生。我想你的答案,但除了想法,它会首先扫描数据库(使用'where',一个查询),然后将执行*更新*(另一个查询).. –

+0

CC:@ArupRakshit:好的,米困惑。不应该是1呼叫方法更快然后2呼叫方法?当在接受的答案中使用代码时,我得到一个完整的时间= 0.52分钟(约30秒),现在我尝试了1呼叫方法,它仍然运行5分钟..我错过了什么? –

+0

@TheMiniJohn一个人正在采取* 5分钟*? :) 怎么样 ?你是否正确地做了,小心,否则会发生一些错误的数据库更新。 –

2

这是由于您正在执行update_attributes,即使它没有通过grepo_id找到记录。如果find_by_grepo_id没有找到任何记录,则返回nil。所以你需要添加一个条件来摆脱这个错误。

array.each do |a| 
    player = Player.find_by_grepo_id(a[:grepo_id]) 
    player.update_attributes(a) if player.present? 
end 
+0

这就是我现在的基本情况:)但不是那么糟糕的练习?它打3个电话给DB(告诉我,如果我错了) –

+1

3调用为什么。代码会做2个电话,一个是找到记录RD,第二个是update_attributes。 'player.present?'不会调用db。它只是检查它是否为零,然后返回false它有一定的价值,它会返回true。 –

+0

好吧,这就是我想知道的:)谢谢Bachan –

0

Rails有try方法(check the docs),你可以在这里使用它:

array.each do |a| 
    player = Player.find_by_grepo_id(a[:grepo_id]) 
    player.try do |p| 
    p.update_attributes(a) 
    end 
end 

这应该做工精细和更新属性或默默失败(不抛出异常)时,没有找到记录。