2016-01-23 55 views
1

的Ruby 2.3.0更新时,Rails的4.2.4,并实际使用PostgreSQL而非SQLite的确保Rails的数据库记录的唯一不中断更新过程

更新了清晰

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

我用耙子文件来存储可执行更新的代码和我在终端输入$ rake update_task(工作如果表中有与进口的CSV行没有重复)。与此相关的问题是数据库ABORTS(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 

和LIB /任务/ 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是在应用程序/ 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 

更新:@克里斯坦的解决方案工作的下方,但这里是放在哪里开始/救援/结束处理:

在应用程序/ 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 
+0

您正在使用什么版本的PostgreSQL? 9.5增加了对的支持。 'INSERT ...在冲突中无所作为' –

+0

这可能非常好(我使用9.5),Tom ...我以前没有尝试过原始SQL!在将来的某个时候,我需要加快速度,不要使用csv解析,并且可能在这里有/从/可能是你的SQL建议。 – JHFirestarter

回答

1

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

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

另一种选择是一个uniqueness validation添加到您的Rails的模型,然后选择保存前检查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 
+0

我可能太罗嗦了,也应该在“perform_this”中包含底层方法。解决方案需要(a)在没有(b)提出例外和中止/失败的情况下,兑现唯一性验证。当我尝试开始/救援/最终解决方案时,(b)已解决,但没有(a) - 表格现在有重复。与“如果u.valid?”一样(即使当“如果u.valid?”与perform_this .save方法结合使用时,我也只是在有解决方案时更新它)。 – JHFirestarter

+0

如果您对这些属性具有唯一的数据库索引,则不可能以重复结束。你确定你已经配置好了吗? –

+0

啊,有趣...我需要删除attr3作为唯一性约束 - 感谢您帮助我找到!尽管如此,没有违规的attr3约束,开始/救援/结束处理确实消除了引发的异常......但是表没有更新(所以rake任务正在以某种方式中止)。在“rescue”和“end”之间是否应该使用一些等效的“move to next”语法? – JHFirestarter

相关问题