2010-02-18 104 views
14

我有一個數組這樣保存活動記錄陣列

a = [] 

a << B.new(:name => "c") 
a << B.new(:name => "s") 
a << B.new(:name => "e") 
a << B.new(:name => "t") 

如何我可以將它保存在一次?

回答

24
a.each(&:save) 

這將調用B#save陣列中的每個項目。

+9

然後將其包裝在B.transaction中以將其全部保存在一個原子操作中。 – Farrel 2010-02-18 22:53:57

+2

不要只是調用'save',要麼檢查返回值(true或false),要麼使用'.save!'讓rails在異常情況下引發異常! – reto 2013-05-16 14:15:18

39
B.transaction do 
    a.each(&:save!) 
end 

這將創建一個事務,該事務遍歷數組的每個元素並調用element.save。

您可以在Rails和Ruby API中閱讀有關ActiveRecord Transactionsthe each method的文章。

+1

注意!除非使用'save!',否則這將不起作用。看來只有在發生異常時纔會中止事務。 – Alexey 2013-05-14 17:58:11

+0

哎呀。你是對的。我已經更新了答案。 – 2013-05-16 14:07:23

+0

現在您需要在某處捕獲「ActiveRecord :: RecordInvalid」。 – Alexey 2013-05-16 18:03:19

0

在事務中包裝save將不夠用:如果未通過驗證,將不會引發異常,也不會引發回滾。

我可以建議是:

B.transaction do 
    a.each do |o| 
    raise ActiveRecord::Rollback unless o.save 
    end 
end 

只是做B.transaction do a.each(&:save!) end是不是一種選擇,要麼,因爲事務塊不會拯救其他任何異常比ActiveRecord::Rollback,應用程序會崩潰的驗證失敗。

我不知道如何檢查後,如果記錄已保存。


更新。正如有人downrated我的回答,我認爲這個人一直在尋找一個剪切和粘貼的解決方案:),所以這裏是一些(醜陋的:))的方式來處理失敗/成功值:

save_failed = nil 
B.transaction do 
    a.each do |o| 
    unless o.save 
     save_failed = true 
     raise ActiveRecord::Rollback 
    end 
    end 
end 
if save_failed 
    # ... 
else 
    # ... 
end 
+0

'保存!'是完美的,'交易做'塊會自動恢復所有其他變化。 – reto 2013-05-16 14:16:15

+0

如果驗證失敗,您需要手動捕獲事務外的異常。 – Alexey 2013-05-16 18:02:20

+0

我要說的是:你有兩個選擇a)使用返回值在'save'後面處理驗證權限b)讓流程/作業使用'save!'崩潰,(幾乎在所有情況下)沒有中間地帶。一個'save'(不處理它的返回值)是一個很大的警告信號。交易只是確保其他一切都得到恢復。 – reto 2013-05-17 08:42:14

8

所以我認爲我們需要阿列克謝提出異議並中止交易和喬丹的單線解決方案。我提議:

B.transaction do 
    success = a.map(&:save) 
    unless success.all? 
    errored = a.select{|b| !b.errors.blank?} 
    # do something with the errored values 
    raise ActiveRecord::Rollback 
    end 
end 

這會給你一個位的兩個世界:通過滾動,它的知識記錄失敗的交易,甚至允許您訪問驗證錯誤在其中。

+1

而不是'!b.errors.blank?',爲什麼不'b.errors.present?' – moveson 2016-11-29 03:43:39

+0

也可能是'a.reject {| b | b.errors.blank?}'。主要是因爲它差不多是3年前的事了,這是一個非常好的答案。也因爲Ruby,我是對的嗎? – kayakyakr 2017-01-20 05:08:43

2

我知道這是一個老問題,但我驚訝的這個誰也沒有想到:

B.transaction do 
    broken = a.reject { |o| o.save } 
    raise ActiveRecord::Rollback if broken.present? 
end 

if broken.present? 
    # error message 
end 
+0

你應該使用保存!而不是加薪。交易將處理剩下的事情。 – 2016-03-30 13:38:04

+0

@ D.Wonnink是的,但它會短路進一步節省,所以你只會知道失敗的第一個,而不是所有的失敗。當你有30個對象保存在行中時,這可能會造成一個世界的不同,其中一半保存在行中,其中一半失敗,你必須以一種不會讓他重新發送表單15次來糾正15個錯誤的方式向用戶顯示驗證後來只是爲了學習另一件事情被打破;) – d4rky 2016-05-11 07:19:17