Ruby 2.3.0, Rails 4.2.4 и на самом деле с использованием postgreSQL, а не SQLite
Обновлено для ясности
У меня есть большой файл csv (обновляется извне и загружается ежедневно), и я написал метод обновления таблицы базы данных Rails. Я не хочу, чтобы метод добавлял все строки в базу данных без проверки уникальности, поэтому я использую это отличное решение (Как сделать столбец уникальным и проиндексировать его при миграции Ruby on Rails?) с помощью add_index
.
Я использую rake-файл для хранения исполняемого кода обновления и ввожу $ rake update_task
в свой терминал (который работает, ЕСЛИ в таблице нет дубликатов с импортированными строками csv). Проблема заключается в том, что база данных ВЫКЛЮЧАЕТ (rake aborted!
) грабли, когда встречает первую повторяющуюся запись (ERROR: duplicate key value violates unique constraint
).
Что я могу сделать, чтобы удалить / не сохранять дубликаты, избегая при этом прерывания / сбоя? Я не могу просто отбрасывать таблицу базы данных и перезагружать ее каждый день. Вот схема:
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
и моя задача rake в содержимом lib / tasks / file_name.rake:
desc "Download data and update database table"
task :update_task => :environment do
u = CorrectClassName.new
u.perform_this
end
и CorrectClassName
находится в файле .rb в app / directory1:
class CorrectClassName
def perform_this
something = ClassWithUpdateCode.new
something.update_database
end
end
и ClassWithUpdateCode
находится в файле .rb в 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
Обновление: решение @Kristan работает ниже, но вот где разместить обработку начала / восстановления / завершения:
В файле .rb в 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
INSERT ... ON CONFLICT DO NOTHING
- person Thomas Walpole   schedule 23.01.2016