通过rake任务将CSV文件上传到Rails 4 DB

时间:2014-01-20 17:23:23

标签: ruby csv ruby-on-rails-4 rake rails-migrations

我正在尝试使用rake任务(rake data:import)从csv fle导入数据并且我遇到错误。过去几个月我一直在教自己,但经过一天半的谷歌搜索后,我找到了许多解决方案,但没有一个可以开始工作。

到目前为止,我的rake文件位于以下位置:

require 'CSV'
namespace :data do
  desc "Import teams from csv file"
  task :import => [:environment] do
    file=IO.read('filepath of my csv').force_encoding("ISO-8859-1").encode("utf-8", replace: nil)

    CSV.foreach(file, :headers => true) do |row|
      product.create ([
                       :name => row['name'],
                       :rating => row['rating'],
                       :year => row['year'],
                       :country => row['country'],
                       :state_or_province => row['state_or_province']]
                      )
    end

  end
end

我没有得到任何具体错误(据我所知)。令我困惑的输出是:

tasks/dataimport.rake:7:in `block (2 levels) in <top (required)>'

该输出是否表明具体发生了什么?

1 个答案:

答案 0 :(得分:10)

这里出现了很多问题所以我会从顶部开始。

CSV.foreach旨在打开一个文件并立即迭代它。 CSV.foreach的第一个参数应该是文件 name 而不是文件的内容。这意味着:

CSV.foreach(file, :headers => true) do |row|

出错,因为file是包含CSV数据的字符串,而不是CSV.foreach期望的文件名。由于您要将Latin-1文本转换为UTF-8,因此您需要让CSV.foreach为您处理,并且您可以使用:encoding选项:

  

此方法还了解一个额外的:encoding参数,您可以使用该参数指定要读取的文件中的数据编码。 [...]例如,encoding: "UTF-32BE:UTF-8"将从文件读取UTF-32BE数据,但在CSV解析之前将其转码为UTF-8。

把它们放在一起,我们有:

CSV.foreach('filepath of my csv', :headers => true, :encoding => 'ISO-8859-1:UTF-8') do |row|

一旦获得CSV读取和迭代,您将看到如下错误:

NameError: undefined local variable or method `product' for ...

您将获得NameError,因为您的佣金任务中的任何位置都没有定义product。我怀疑你是想说Product.create,它会尝试创建Product模型的新实例。 Ruby区分大小写,因此productProduct是不同的东西,Product将是类。

处理NameError后,您会看到这样的抱怨:

NoMethodError: undefined method `keys' for [{ ... }]:Array

您将获得NoMethodError因为Product.create想要查看属性及其值的哈希,而不是包含哈希的数组。你想说:

Product.create(
  :name => row['name'],
  :rating => row['rating'],
  :year => row['year'],
  :country => row['country'],
  :state_or_province => row['state_or_province']
)

当然,如果您的row仅包含这五个值,那么只需将整个row交给create

Product.create(row.to_hash)

如果row包含(或可能包含)您不希望create看到的其他一些内容,请使用Hash#slice来抓取{{1}的部分内容你感兴趣的是:

row

请注意Product.create(row.to_hash.slice(*%w[name rating year country state_or_province])) 从空格分隔列表构建一个字符串数组,因此它们是相同的:

%w[...]

然后splat(%w[a b] ['a', 'b'] )删除数组包装器,因此它们是相同的:

*

你可以使用更容易的眼睛。

另请注意那里的to_hash来电。您的row.to_hash.slice(*%w[name rating year country state_or_province]) row.to_hash.slice('name', 'rating', 'year', 'country', 'state_or_province') 将是row个对象,在其上调用CSV::Row会将该行作为哈希值。


这应该让你的整个佣金任务看起来像这样:

to_hash

CSV.foreach('filepath of my csv', :headers => true, :encoding => 'ISO-8859-1:UTF-8') do |row|
  Product.create(row.to_hash)
end

您可能还想为这些CSV.foreach('filepath of my csv', :headers => true, :encoding => 'ISO-8859-1:UTF-8') do |row| Product.create(row.to_hash.slice(*%w[name rating year country state_or_province])) end 电话添加一些错误处理。