我正在尝试使用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)>'
该输出是否表明具体发生了什么?
答案 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区分大小写,因此product
和Product
是不同的东西,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
电话添加一些错误处理。