如何只将csv中的几列插入表中

时间:2015-09-27 02:37:55

标签: ruby-on-rails ruby database csv rails-activerecord

我有一个带有以下标题行的大型.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()知道哪些列与表匹配?

2 个答案:

答案 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做的事情,而你并没有阻止它将垃圾数据扔进你的数据库。

解决方案分为两部分:

  1. 在任何地方使用String个密钥或在任何地方使用Symbol个密钥。
  2. create想要在您的案例中使用Hash参数,因此请使用Hash#slice代替values_at
  3. 更像这样:

    CSV.foreach(csv_file, ...) do |row|
      City.create row.to_hash.slice(*%w[name timezone country_code])
    end
    

    PS:您确实应该在数据库中包含一些NOT NULL约束,并在模型中包含一些验证。