如何编写导入数据和处理删除的Rake任务?

时间:2015-04-10 09:13:12

标签: ruby-on-rails import rake recreate

我想做与此问题How to write Rake task to import data to Rails app?中解释的相同的事情。

但是,我对接受的答案不满意,因为它不考虑来源中的已删除项目。

考虑到源中的已删除条目,最简单,最常用的轨道符合这一点的方法是什么?

注意:

  • 使用.find_or_initialize_by_identifier并且永不删除时,表中会留下多余的条目。
  • 在每次导入之前使用.delete_all时,据我所知,主键不会重置并快速接近其限制。
  • 我可以删除表并在rake任务中使用:: Migrations.create_table,但架构和迁移中的定义必须与rake任务中的代码保持同步,这似乎是不可取的。

2 个答案:

答案 0 :(得分:0)

您绝对不应该删除所有记录,然后从数据中重新创建它们。这将产生各种各样的问题,例如,破坏其他表中的任何外键字段,这些字段在删除之前用于指向该对象。这就像敲房子并重建它以便有一个不同颜色的门。所以,"看看它是否在那里,如果它然后更新它(如果它不同),如果它不是那么创建它"是正确的策略。

您不能说明您的删除标准是什么,但是如果它是"任何在导入数据中未提及的记录都应该被删除"那么你只需要跟踪输入数据中的一些独特字段,然后删除该列表中自己唯一字段不存在的所有记录。

所以,你进行导入的代码看起来像这样(从另一个问题复制代码:这段代码以极其笨重的方式设置数据,但我不会在这里解决这个问题)

namespace :data do
  desc "import data from files to database"
  task :import => :environment do
    file = File.open(<file to import>)
    identifiers = []
    file.each do |line|
      #disclaimer: this way of setting the data from attrs[0], attrs[1] etc is crappy and fragile and is not how i would do it
      attrs = line.split(":")
      identifier = attrs[0]
      identifiers << identifier
      if p = Product.find_or_initialize_by_identifier(identifier)
        p.name = attrs[1]
        etc...
        p.save!
      end
    end
    #destroy any which didn't appear in the import data
    Product.where("identifier not in (?)", identifiers).each(&:destroy)
  end
end

答案 1 :(得分:0)

我使用的是使用.delete_all和没有rails的默认id auto_increment列的表架构,以避免在.delete_all之后增加值。

create_table :airport_locations, id: false do |t|
  t.string :iata_faa_code, :primary_key
  t.float :latitude
  t.float :longitude
end
add_index :airport_locations, :iata_faa_code

注释

  • 数据集相当小(约5000个条目),并且不经常发生更新。
  • 跟踪已删除的项目,如Max Williams所述,如果表格较小,则可以回答。虽然具有数千个条目的表可能需要大量内存,但更复杂的策略(例如使用临时表)可能需要查找已删除的条目。