【问题标题】:Rails rake task - parsing CSV file and importing if it doesn't already existRails rake 任务 - 解析 CSV 文件并在它不存在时导入
【发布时间】:2013-03-07 17:42:36
【问题描述】:

我正在尝试在 rails 3 中创建一个新的 rake 任务。

我有两个 csv 文件(gas.csv 和 elec.csv),其中包含每十五分钟从 SQL 数据库更新的使用数据。 csv的格式如下(我无法控制):

MeterID,RoundedTimeStamp,Value,Register
165,31/01/2012 00:00,1200,0
165,28/02/2012 00:15,1201,0
165,31/03/2012 00:30,1199,0

csv 每月更新一次新值,我想编写一个 rake 导入任务来将文件导入模型的相关部分,Usage(month, elec, gas)。到目前为止,我已经为 elec 导入提供了以下内容,但这并没有真正起作用:

require 'csv'
desc "Importing Usage data from CSV file"
task import: :environment do

    file = ".../elec.csv"

    CSV.foreach(file, headers: true) do |row|
            Usage.find_or_create_by_month({
                month: row[1],
                elec: row[2]
            })
    end
end

当我从控制台运行 rake:import 时,它会很好地导入 elec 使用数据,但不会跳过重复项。有没有一种方法可以导入这个文件而不重复,还有一种方法可以对气体数据做同样的事情?

注意:我知道使用 first_or_create 而不是 find_or_create 更好,但这不适用于 _by_month。

干杯!

【问题讨论】:

  • where(month: row[1]).first_or_create,find_or_create 应该在 Rails 4 的弃用方法列表中。
  • 是的,我在 APIdock 上看到了,我先是这样,但它也不起作用!

标签: ruby-on-rails rake


【解决方案1】:
User.where(month: row[1]).first_or_create do |user|
  user.elec = row[2]
end

更多信息http://apidock.com/rails/ActiveRecord/Relation/first_or_create

【讨论】:

  • 这似乎不起作用 - 即使它们已经存在,它也会创建新记录(与我自己的功能大致相同)。
  • @KevL,您自己尝试过吗?我在控制台上尝试过,它就像提供的示例一样工作。我会建议你将问题减少到最低限度,看看它在哪里失败。
  • 啊 - 我知道我哪里出错了。 RoundedTimeStamp 是 DateTime 格式,而我的月份列是日期格式,所以我通过添加 month: row[1].to_date) 来修复它。谢谢!您知道使用usage.elec = row[2] 还是直接使用.first_or_create(elec: row[2]) 会更快吗?
  • 不,但如果我设置单个属性,我将使用不带块的内联版本。
  • 两个foreach,不要重复自己,使用一个方法或者一个lambda。您总是可以在处理之前将一个文件的内容附加到另一个文件并执行一次 foreach,但这会比它需要的更复杂。
【解决方案2】:

你可以试试Upsert:

require 'upsert'
require 'active_support/core_ext' # you'll already have this in Rails
# ...
u = Upsert.new Usage.connection, Usage.table_name
# ...
CSV.foreach('elec.csv', headers: :first_row) do |row|
  date = Date.parse row['RoundedTimeStamp']
  selector = { meter_id: row['MeterID'], month: date.strftime('%Y-%m') }
  setter = { elec: row['Value'] }
  u.row selector, setter
end
CSV.foreach('gas.csv', headers: :first_row) do |row|
  date = Date.parse row['RoundedTimeStamp']
  selector = { meter_id: row['MeterID'], month: date.strftime('%Y-%m') }
  setter = { gas: row['Value'] }
  u.row selector, setter
end

如果您的“月”列是一个字符串字段,那么这将起作用。如果它是一个真实的日期字段,那么您可以尝试只使用每个月的第一天:

date.strftime('%Y-%m-01')

【讨论】:

  • 我收到一个错误,“无法加载此类文件 -- upsert”?另外,是的,我很确定我对行 [1] 是正确的,因为标题是“RoundedTimeStamp”。
  • 将“upsert”添加到您的 Gemfile 中?
  • 哎呀……很明显!知道我怎么能这样做来进口现有几个月的天然气吗??
  • 您只需要 2 个 CSV.foreach 块,每个 csv 文件一个。在每一个中,只需确保将选择器仔细设置为“名称”字段
  • 谢谢,我现在已经尝试了这两种代码,它们没有向数据库添加任何内容(没有错误)。即使我destroy_all 用法,它们仍然什么都不添加?这是我正在使用的代码:pastebin.com/PAD38JTA
猜你喜欢
  • 2017-05-03
  • 1970-01-01
  • 2012-09-09
  • 2015-10-24
  • 2019-04-24
  • 1970-01-01
  • 1970-01-01
  • 2018-05-27
  • 1970-01-01
相关资源
最近更新 更多