我有一个带有以下标题行的大型.csv文件:
:headers =>
["_id_", "name", "ascii_names", "alternate_name", "latitute",
"longitude", "feature_class", "feature_code", "country_code", "cc2",
"admin_code_1", "admin_code_2", "admin_code_3", "admin_code_4", "population",
"elevation", "dem", "timezone", "modification_date"]}
我的表只有name, country_code, timezone
列。
我想只将.csv文件的那3列插入表中。
我尝试过的事情:
CSV.foreach(csv_file, {:col_sep => "\t", :quote_char => '&', :write_headers => true, :headers => ["_id_", "name", "ascii_names", "alternate_name", "latitute", "longitude", "feature_class", "feature_code", "country_code", "cc2", "admin_code_1", "admin_code_2", "admin_code_3", "admin_code_4", "population", "elevation", "dem", "timezone", "modification_date"]}
) do |row|
City.create row.to_hash.values_at(:name, :timezone, :country_code)
binding.pry
end
问题是当我在运行此表后检查表时,没有填充任何值。行本身已经创建,但它们都是空的。
如何正确映射这些,以便.create()
知道哪些列与表匹配?
答案 0 :(得分:3)
您需要首先选择是否要将列标题标识为字符串或符号。在headers => ["_id_" ...]
中,您将其声明为Strings
,但在row.to_hash.values_at(:name, ...)
中,您正在寻找Symbols
。 - 谢谢你到@mu
其次,您可以手动指定要插入到表中的值而不对其进行散列,就像在任何其他时间通过Ruby创建新条目时一样。
City.create(:name => row[:name], :timezone => row[:timezone], :country_code => row[:country_code])
总而言之,您的方法应如下所示:
CSV.foreach(csv_file, {:col_sep => "\t", :quote_char => '&', #:write_headers => true,
:headers => [:geonameid, :name, :ascii_names, :alternate_name, :latitute, :longitude, :feature_class, :feature_code, :country_code, :cc2, :admin_code_1, :admin_code_2, :admin_code_3, :admin_code_4, :population, :elevation, :dem, :timezone, :modification_date]}
) do |row|
City.create(:name => row[:name], :timezone => row[:timezone], :country_code => row[:country_code])
end
答案 1 :(得分:1)
首先,您要告诉CSV标题是字符串:
:headers => ["_id_", "name", ...]
这意味着在CSV.foreach
块内,这个:
row.to_hash
是具有Hash
键的String
。然后,您在values_at
上拨打Hash
,并要求提供三个Symbol
个密钥,values_at
正确地为您提供[nil]
,因为Hash
带有字符串密钥没有Symbol
键的任何值。这意味着您CSV.foreach
阻止只是一种过于复杂的说法:
City.create [nil]
当您将Array
传递给create
时,您实际上是在说:
array.map { |e| City.create(e) }
这可以减少您的阻止:
City.create nil
在ActiveRecord或ActiveModel内部,它可能在传递的属性上调用to_h
,因此使用create
调用nil
与使用空Hash
调用它是相同的。
所有这一切的结果都是一堆空记录,因为,这就是你要求ActiveRecord做的事情,而你并没有阻止它将垃圾数据扔进你的数据库。
解决方案分为两部分:
String
个密钥或在任何地方使用Symbol
个密钥。create
想要在您的案例中使用Hash
参数,因此请使用Hash#slice
代替values_at
。更像这样:
CSV.foreach(csv_file, ...) do |row|
City.create row.to_hash.slice(*%w[name timezone country_code])
end
PS:您确实应该在数据库中包含一些NOT NULL
约束,并在模型中包含一些验证。