2014-05-15 67 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 

這應該做工精細和更新屬性或默默失敗(不拋出異常)時,沒有找到記錄。