简体   繁体   中英

Using ruby 1.9.2 and rails 3, how do I collect activerecord errors to display later? Or better yet, hold off on the commit until ready?

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM