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? - ruby-on-rails

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

Related

Update record if exists with roo gem

I have an import working correctly from a Spreadsheet using Roo gem.
The problem is every time I call the rake task, new records are created.
I want to update_attributes of the records in case the record exists.
Is there any way to approach this? I've tried this with no luck:
namespace :import do
desc "Import data from spreadsheet" # update this line
task data: :environment do
data = Roo::Spreadsheet.open('lib/t3.xlsx') # open spreadsheet
headers = data.row(1) # get header row
data.each_with_index do |row, idx|
next if idx == 0 # skip header
# create hash from headers and cells
product_data = Hash[[headers, row].transpose]
product = Product.new(product_data)
puts "Guardando Producto #{product.name}"
if product?
product.update_attributes
else
product.save!
end
rescue ActiveRecord::RecordInvalid => invalid
puts invalid.record.errors
end
end
end
if product? will never return false. You're testing whether the variable contains a falsy value (nil/false) or any other value. After calling product = Product.new, the value stored in product can never be nil or false.
What you want is to first find, and if not found, new, and then update_attributes on the resulting object:
product = Product.find_by(product_data.name) || Product.new
product.update_attributes(product_data)

Rails Check If Record Is First

I am iterating through a list of records. I need to check that if a record is first do XYZ and if not do ABC. Unfortunately I cant do this:
user = User.first
or
user = User.find(:id)
user.first?
Solution posted below
1. Make method to grab next and previous records
def next
[Model].where("id > ?", id).first
end
def prev
[Model].where("id < ?", id).last
end
2. Make method to check if record is first
def first?(record)
[Model].first == record
end
3. check if record is first
records.each do |record|
if record.first?(record)
record.update_attributes(attr: record.attr + record.attr)
else
prev_rec = [Model].find(record.id).prev
record.update_attributes(attr: prev_rec.attr + record.attr )
end
end
returns true or false
One improvement i would make sure that [Model].first is persistent so that it doesn't make a call to the database each time the loop is run.

Check for successful creation of records on create rails

I am looping through a CSV and creating new records for each row.
How can I increment a counter if the creation of a record was successful?
Here is my existing code:
Employer.create(employer) do |e|
e.password = generated_password
e.tenant = tenant
end
I'd like to increment a counter like i+=1 if the .create worked.
You can inspect the returned instance to see if it was persisted or not:
employee = Employee.create(...) do |e|
...
end
i += 1 if employee.persisted?
You could also do this inside of the block.
Alternatively, separate this into separate new/save calls, and check the return value of save, which will be true or false.

Get row of inserted id in rails transaction with postgres

I have the following code:
#which receives Group objects and saves them in a transaction
def saveGroup(group_buffer)
self.transaction do
output = group_buffer.each(&:save)
if(!output)
return false
break
elsif( #I want the inserted row id as they are inserted)
#put inserted row id in array
end
end
return #the_array
end
Is this possible? Basically, what I want is to obtain the inserted row id in a transaction as the objects are saved and push it into an array. What is the best way to do this? Thank you very much...
Is this what you are looking for? if any instance will not save, the transaction will roll back, so you don't have to brake it manually.
def saveGroup(group_buffer)
ids = []
self.transaction do
group_buffer.each do |i|
i.save
ids << i.id
end
end
if ids.empty?
false
else
ids
end
end

Mongoid get multiple data with only single query

When my page load, it render the Highcharts data (like [1,2,5,10,40] )
controller
#server = Server.find(params[:id])
#statuses = #server.statuses.where(:created_at => 1.days.ago..Time.now)
#statuses.each do |status|
if status.players.nil?
#chartData << 0
else
#chartData << status.players
end
end
view
... ,"data": <%=chartData.to_a%> ...
But in this code, I reached the performance issue.
get first data
get second data
get third data
get fourth data
...
Every page load, it get all of the #statuses with multiple query. (not single query) and the page get slow down.
Can I get all of the data using only single query like MySQL (SELECT * FROM sometable)?
added
Sorry I confused some code.
Try this code
#statuses = Server.includes(:statuses).where(:created_at => 1.days.ago..Time.now)
#statuses.each do |status|
if status.players.nil?
#chartData << 0
else
#chartData << status.players
end
end

Resources