更新时确保Rails数据库记录的唯一性,而不会中止更新过程

时间:2016-01-23 15:51:08

标签: ruby-on-rails database postgresql rake-task

Ruby 2.3.0,Rails 4.2.4,实际上使用的是postgreSQL而不是SQLite

为清晰起见而更新

我有一个大的csv文件(每天外部更新和下载),并编写了一个更新Rails数据库表的方法。 我不希望该方法在不验证唯一性的情况下将所有行附加到数据库,因此我将这个出色的解决方案(How do I make a column unique and index it in a Ruby on Rails migration?)与add_index一起使用。

我正在使用rake文件来存储可执行更新代码,并在我的终端中输入$ rake update_task(如果表与导入的csv行没有重复,则可以正常工作)。这个问题是数据库ABORTS(rake aborted!)在遇到第一个重复条目(ERROR: duplicate key value violates unique constraint)时的rake。

如何在避免中止/失败的同时删除/不保存任何重复项?我不能简单地删除数据库表并每天重新加载它。这是架构:

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位于app / directory1中的.rb文件中:

class CorrectClassName

  def perform_this
    something = ClassWithUpdateCode.new
    something.update_database
  end

end

ClassWithUpdateCode位于app / directory2中的.rb文件中:

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的解决方案在下面工作,但这里是开始/救援/结束处理的地方:

在app / directory2中的.rb文件中:

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

1 个答案:

答案 0 :(得分:1)

rake正在淘汰,因为当您尝试保存违反表格唯一性约束的记录时会引发异常。防止这种情况的最简单方法是捕获并忽略异常。我假设您的记录是在u.perform_this期间创建的。

task :update_task => :environment do
  u = CorrectClassName.new
  begin
    u.perform_this
  rescue ActiveRecord::RecordNotUnique
    # move on
  end
end

另一种选择是在您的Rails模型中添加uniqueness validation,然后在保存之前检查valid?或致电create(不是create!),而不是class CorrectClassName < ActiveRecord::Base validates_uniqueness_of :attr1, scope: [:attr2, :attr3] end 。提高验证例外。

task :update_task => :environment do
  u = CorrectClassName.new(data)
  u.perform_this if u.valid?
end
{{1}}