In a single operation, I am inserting multiple rows into a table. It is possible for one or more of those rows to cause an ActiveRecord::RecordInvalid issue. When this happens, I would like to be able to back out all the transactions for this particular operation and have the user fix the data before proceeding. What is the best way to go about doing this?
Right now, if it fails on the 2nd row of data, the 1st one has already been committed to the database so the user will not know if they should reload that first row or not. I can just inform the user how many rows succeeded and they can know to fix and reload only from that part on, but it would be better for me if I could just undo everything and have the user start over after they fix their data.
FYI, the user initially loads a CSV file into a table that contains on row for every row*column of their csv file and I import from that import_table.
This is a piece of my method in the controller:
def process_import
@import = ImportTable.find(params[:id])
@cells = @import.import_cells
@new_donors = Array.new
@existing_donors = Array.new
class_name = 'Donor'
klass = ActiveRecord.const_get(class_name) # get access to class
#loop through rows
0.upto(@import.row_count - 1) do |row_index|
donor = {}
donation = {}
record_status = 'new'
row = @cells.select { |cell| cell.row_index == row_index }
#loop through columns
0.upto(@import.column_count - 1) do |column_index|
contents = row.select { |cell| cell.column_index == column_index}[0].contents
case column_index
when 0 then record_status = contents
when 1 then donor["id"] = contents
when 2 then donor["prefix1"] = contents
when 3 then donor["first_name1"] = contents
when 4 then donor["middle_name1"] = contents
...
end #case
end #columns
unless @donor = Donor.find_by_id(donor["id"])
donor.delete("id")
@donor = klass.find_or_initialize_by_company_and_prefix1_and_first_name1_and_last_name1_and_address1(donor)
end
@donor.new_record? ? @new_donors.push(@donor) : @existing_donors.push(@donor)
@donor.save!
if !donation["amount"].blank?
@donation = @donor.donations.build(donation)
@donation.save!
end
end #rows
@import.processed_date = Time.now
@import.save
end
I would just have the user resubmit the entire data
Wrap the import process in a transaction. If an error occurs then you can back out of the entire commit by raising an ActiveRecord::Rollback
klass.transaction do
# loop through rows
# You might want to call @donor.save rather than @donor.save! so that you do not raise errors, yet still have invalid records.
raise ActiveRecord::Rollback if (@new_donors+@existing_donors).any?{|donor| !donor.valid? }
end
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.