Luca Romagnoli
Luca Romagnoli

Reputation: 12475

save an active records array

I have an array like this

a = []

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

How i can save it at once?

Upvotes: 24

Views: 24407

Answers (6)

Yaroslav
Yaroslav

Reputation: 2438

In case you're looking for more efficient solution than save each row in the loop please look my answer here Ruby on Rails - Import Data from a CSV file

I'm suggesting to use gem activerecord-import there.

Upvotes: 0

d4rky
d4rky

Reputation: 479

I know this is an old question but I'm suprised no one thought of this:

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

if broken.present?
  # error message
end

Upvotes: 2

kayakyakr
kayakyakr

Reputation: 403

So I think we need a middle ground to Alexey's raising exceptions and aborting the transaction and Jordan's one-liner solution. May I propose:

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

This will give you a bit of both worlds: a transaction with rollback, knowledge of which records failed and even gives you access to the validation errors therein.

Upvotes: 14

Alexey
Alexey

Reputation: 4071

Wrapping save in transaction will not be enough: if a validation is not passed, there will be no exception raised and no rollback triggered.

I can suggest this:

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

Just doing B.transaction do a.each(&:save!) end is not an option either, because the transaction block will not rescue any exception other than ActiveRecord::Rollback, and the application would crash on failed validation.

I do not know how to check afterwards if the records have been saved.


Update. As someone has downrated my answer, i assume that the person was looking for a cut-and-paste solution :), so here is some (ugly :)) way to process fail/success value:

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

Upvotes: 2

Benjamin Manns
Benjamin Manns

Reputation: 9148

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

This will create a transaction that loops through each element of the array and calls element.save on it.

You can read about ActiveRecord Transactions and the each method in the Rails and Ruby APIs.

Upvotes: 55

Jordan Running
Jordan Running

Reputation: 106067

a.each(&:save)

This will call B#save on each item in the array.

Upvotes: 29

Related Questions