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

jfa*_*son 2 ruby csv rake rails-migrations ruby-on-rails-4

我正在尝试使用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
Run Code Online (Sandbox Code Playgroud)

我没有得到任何具体的错误(据我所知).混淆我的输出是:

tasks/dataimport.rake:7:in `block (2 levels) in <top (required)>'
Run Code Online (Sandbox Code Playgroud)

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

mu *_*ort 10

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

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

CSV.foreach(file, :headers => true) do |row|
Run Code Online (Sandbox Code Playgroud)

出错是因为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|
Run Code Online (Sandbox Code Playgroud)

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

NameError: undefined local variable or method `product' for ...
Run Code Online (Sandbox Code Playgroud)

你得到一个,NameError因为product你的rake任务中没有任何定义.我怀疑你的意思是说Product.create,这会尝试创建一个新的Product模型实例.Ruby是区分大小写,product并且Product是不同的东西,Product将是一流的.

一旦NameError处理完毕,你会看到这样的抱怨:

NoMethodError: undefined method `keys' for [{ ... }]:Array
Run Code Online (Sandbox Code Playgroud)

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

Product.create(
  :name => row['name'],
  :rating => row['rating'],
  :year => row['year'],
  :country => row['country'],
  :state_or_province => row['state_or_province']
)
Run Code Online (Sandbox Code Playgroud)

当然,如果你row只包含那五个值,那么只需将整个内容row交给create:

Product.create(row.to_hash)
Run Code Online (Sandbox Code Playgroud)

如果row包含(或可能包含)一堆您不想create看到的其他内容,请使用Hash#slice以获取row您感兴趣的部分内容:

Product.create(row.to_hash.slice(*%w[name rating year country state_or_province]))
Run Code Online (Sandbox Code Playgroud)

请注意,%w[...]从白色空格分隔列表构建字符串数组,因此它们是相同的:

%w[a b]
['a', 'b']
Run Code Online (Sandbox Code Playgroud)

然后splat(*)删除数组包装器,因此它们是相同的:

row.to_hash.slice(*%w[name rating year country state_or_province])
row.to_hash.slice('name', 'rating', 'year', 'country', 'state_or_province')
Run Code Online (Sandbox Code Playgroud)

您可以使用更容易在眼睛上使用的任何形式.

还要注意to_hash那里的电话.你row将成为一个CSV::Row对象,调用to_hash它会给你一行作为哈希.


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

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

要么

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
Run Code Online (Sandbox Code Playgroud)

您可能还想为这些create调用添加一些错误处理.