【发布时间】:2026-01-24 04:25:02
【问题描述】:
我有一个这样的数组
a = []
a << B.new(:name => "c")
a << B.new(:name => "s")
a << B.new(:name => "e")
a << B.new(:name => "t")
如何一次保存?
【问题讨论】:
标签: ruby-on-rails ruby
我有一个这样的数组
a = []
a << B.new(:name => "c")
a << B.new(:name => "s")
a << B.new(:name => "e")
a << B.new(:name => "t")
如何一次保存?
【问题讨论】:
标签: ruby-on-rails ruby
B.transaction do
a.each(&:save!)
end
这将创建一个循环遍历数组的每个元素并对其调用 element.save 的事务。
您可以在 Rails 和 Ruby API 中了解 ActiveRecord Transactions 和 the each method。
【讨论】:
save!,否则这将不起作用。似乎只有在出现异常时才会中止事务。
ActiveRecord::RecordInvalid。
a.each(&:save)
这将对数组中的每个项目调用B#save。
【讨论】:
save,要么检查返回值(真或假)或使用.save! 让rails 在出现问题时引发异常!
所以我认为我们需要一个中间立场来解决阿列克谢提出例外和中止交易以及乔丹的单线解决方案。我可以提议:
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
这将为您提供两全其美:具有回滚的事务,知道哪些记录失败,甚至可以让您访问其中的验证错误。
【讨论】:
!b.errors.blank?,为什么不是b.errors.present?
a.reject{|b| b.errors.blank?}。主要是因为它几乎是 3 年前的事了,而且这是一个即兴的答案。也是因为 Ruby,我说的对吗?
我知道这是一个老问题,但我很惊讶没有人想到这一点:
B.transaction do
broken = a.reject { |o| o.save }
raise ActiveRecord::Rollback if broken.present?
end
if broken.present?
# error message
end
【讨论】:
在事务中包装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 之外的任何异常,并且应用程序会在验证失败时崩溃。
我不知道以后如何检查记录是否已保存。
更新。由于有人低估了我的回答,我假设该人正在寻找剪切和粘贴解决方案:),所以这里有一些(丑陋的:))处理失败/成功值的方法:
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
【讨论】:
save! 是完美的,transaction do 块将自动恢复所有其他更改。
save 之后使用返回值处理验证 b) 使用 save! 让进程/作业崩溃,有(几乎在所有情况下)没有中间立场。一个单独的save(不处理它的返回值)是一个很大的警告信号。该事务只是确保其他所有内容也得到恢复。
a 中的任何对象都无法保存,则整组对象都不会被保存。在事务块之后你不知道:a)如果对象已经保存,b)如果没有,是哪一个导致了回滚 c)问题是什么。我无法想象我想要这种行为的情况。
transaction 方法似乎没有将此作为值返回。在我看来,它就像一个 ActiveRecord 缺陷。
如果您正在寻找比在循环中保存每一行更有效的解决方案,请在此处查看我的答案Ruby on Rails - Import Data from a CSV file
我建议在那里使用 gem activerecord-import。
【讨论】: