Ruby 2.3.0, Rails 4.2.4, and actually using postgreSQL rather than SQLite
Updated for clarity
I have a large csv file (externally updated & downloaded daily) and wrote a method to update a Rails database table. I do not want the method to append all rows to the database without validating uniqueness, so I use this great solution (How do I make a column unique and index it in a Ruby on Rails migration?) with add_index
.
I'm using a rake file to store the executable update code and I enter $ rake update_task
in my terminal (which works IF the table has no duplicates with the imported csv rows). The problem with this is that the database ABORTS (rake aborted!
) the rake when it encounters the first duplicate entry (ERROR: duplicate key value violates unique constraint
).
What can I do to remove/not save any duplicates while avoiding aborting/failing? I cannot simply drop the database table and reload it each day. Here is the schema:
ActiveRecord::Schema.define(version: 20160117172450) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
create_table "tablename", force: :cascade do |t|
t.string "attr1"
t.string "attr2"
t.string "attr3"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "tablename", ["attr1", "attr2", "attr3"], name: "index_tablename_on_attr1_and_attr2_and_attr3", unique: true, using: :btree
end
and my rake task in lib/tasks/file_name.rake contents:
desc "Download data and update database table"
task :update_task => :environment do
u = CorrectClassName.new
u.perform_this
end
and CorrectClassName
is in an .rb file in app/directory1:
class CorrectClassName
def perform_this
something = ClassWithUpdateCode.new
something.update_database
end
end
and ClassWithUpdateCode
is in an .rb file in app/directory2:
require 'csv'
class ClassWithUpdateCode
def update_database
csv_update = File.read(Rails.root.join('lib', 'assets', "file_name.csv"))
options = {:headers => true}
csv = CSV.parse(csv_update, options)
csv.each do |row|
tm = TableModel.new
tm.attr1 = row[0]
tm.attr2 = row[1]
tm.attr3 = row[2]
tm.save # maybe I can use a different method or if statement here?
end
end
end
Update: @Kristan's solution works below, but here is where to put the begin/rescue/end handling:
In the .rb file in app/directory2:
require 'csv'
class ClassWithUpdateCode
def update_database
csv_update = File.read(Rails.root.join('lib', 'assets', "file_name.csv"))
options = {:headers => true}
csv = CSV.parse(csv_update, options)
csv.each do |row|
tm = TableModel.new
begin
tm.attr1 = row[0]
tm.attr2 = row[1]
tm.attr3 = row[2]
tm.save
rescue ActiveRecord::RecordNotUnique
end
end
end
end