Ruby on Rails – 从CSV文件导入数据
我想从一个CSV文件导入到现有的数据库表中的数据。 我不想保存CSV文件,只需要从它的数据,并把它放到现有的表。 我正在使用Ruby 1.9.2和Rails 3。
这是我的桌子:
create_table "mouldings", :force => true do |t| t.string "suppliers_code" t.datetime "created_at" t.datetime "updated_at" t.string "name" t.integer "supplier_id" t.decimal "length", :precision => 3, :scale => 2 t.decimal "cost", :precision => 4, :scale => 2 t.integer "width" t.integer "depth" end
你能给我一些代码给我看这样做的最好方法,谢谢。
require 'csv' csv_text = File.read('...') csv = CSV.parse(csv_text, :headers => true) csv.each do |row| Moulding.create!(row.to_hash) end
yfeldblum的答案更简单,更简单,对于大文件也能很好地工作:
require 'csv' CSV.foreach(filename, :headers => true) do |row| Moulding.create!(row.to_hash) end
不需要with_indifferent_access或symbolize_keys,也不需要先将文件读入string。
它不会一次将整个文件保存在内存中,而是逐行读取并在每行中创build一个“模型”。
smarter_csv
gem是专门为此用例创build的:从CSV文件读取数据并快速创build数据库条目。
require 'smarter_csv' options = {} SmarterCSV.process('input_file.csv', options) do |chunk| chunk.each do |data_hash| Moulding.create!( data_hash ) end end
您可以使用chunk_size
选项一次读取N个csv行,然后在内部循环中使用Resque来生成创build新logging的作业,而不是立即创build它们 – 这样就可以将生成的负载条目给多个工作者。
另见: https : //github.com/tilo/smarter_csv
你可以尝试Upsert
:
require 'upsert' # add this to your Gemfile require 'csv' u = Upsert.new Moulding.connection, Moulding.table_name CSV.foreach(file, headers: true) do |row| selector = { name: row['name'] } # this treats "name" as the primary key and prevents the creation of duplicates by name setter = row.to_hash u.row selector, setter end
如果这是你想要的,你也可以考虑摆脱表中的自动递增主键,并设置主键的name
。 或者,如果有一些组合的主键的属性,使用它作为select。 没有索引是必要的,它只会使速度更快。
将数据库相关进程封装在一个transaction
块中更好。 代码片段的打击是一套种语言模式的全部过程,
require 'csv' namespace :lan do desc 'Seed initial languages data with language & code' task init_data: :environment do puts '>>> Initializing Languages Data Table' ActiveRecord::Base.transaction do csv_path = File.expand_path('languages.csv', File.dirname(__FILE__)) csv_str = File.read(csv_path) csv = CSV.new(csv_str).to_a csv.each do |lan_set| lan_code = lan_set[0] lan_str = lan_set[1] Language.create!(language: lan_str, code: lan_code) print '.' end end puts '' puts '>>> Languages Database Table Initialization Completed' end end
下面的代码片段是languages.csv
文件的一部分,
aa,Afar ab,Abkhazian af,Afrikaans ak,Akan am,Amharic ar,Arabic as,Assamese ay,Aymara az,Azerbaijani ba,Bashkir ...
使用这个gem: https : //rubygems.org/gems/active_record_importer
class Moulding < ActiveRecord::Base acts_as_importable end
那么你现在可以使用:
Moulding.import!(file: File.open(PATH_TO_FILE))
只要确保你的标题匹配你的表的列名
更好的方法是把它包含在一个rake任务中。 在/ lib / tasks /里面创buildimport.rake文件,并把这个代码放到这个文件中。
desc "Imports a CSV file into an ActiveRecord table" task :csv_model_import, [:filename, :model] => [:environment] do |task,args| lines = File.new(args[:filename], "r:ISO-8859-1").readlines header = lines.shift.strip keys = header.split(',') lines.each do |line| values = line.strip.split(',') attributes = Hash[keys.zip values] Module.const_get(args[:model]).create(attributes) end end
之后,在你的terminalrake csv_model_import[file.csv,Name_of_the_Model]
中运行这个命令rake csv_model_import[file.csv,Name_of_the_Model]
最好使用CSV :: Table并使用String.encode(universal_newline: true)
。 它将CRLF和CR转换为LF
如果你想使用SmartCSV
all_data = SmarterCSV.process( params[:file].tempfile, { :col_sep => "\t", :row_sep => "\n" } )
这表示每行"\t"
制表符分隔数据,行之间用新行分隔"\n"