簡體   English   中英

Ruby on Rails - 從 CSV 文件導入數據

[英]Ruby on Rails - Import Data from a CSV file

我想將數據從 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_accesssymbolize_keys ,也不需要先將文件讀入字符串。

它不會立即將整個文件保存在內存中,而是逐行讀取並每行創建一個 Molding。

smarter_csv gem 是專門為此用例創建的:從 CSV 文件讀取數據並快速創建數據庫條目。

  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 來生成將創建新記錄的作業,而不是立即創建它們 - 這樣您就可以分散生成的負載多個工人的條目。

另見: 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 或者,如果存在形成主鍵的某些屬性組合,請將其用作選擇器。 不需要索引,它只會讓它更快。

這可以提供幫助。 它也有代碼示例:

http://csv-mapper.rubyforge.org/

或用於執行相同操作的 rake 任務:

http://erikonrails.snowedin.net/?p=212

最好將數據庫相關的進程包裝在一個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
...

我知道這是個老問題,但它仍然在谷歌的前 10 個鏈接中。

逐行保存行的效率不是很高,因為它會導致循環中的數據庫調用,您最好避免這種情況,尤其是當您需要插入大量數據時。

使用批量插入更好(並且明顯更快)。

INSERT INTO `mouldings` (suppliers_code, name, cost)
VALUES
    ('s1', 'supplier1', 1.111), 
    ('s2', 'supplier2', '2.222')

您可以手動構建這樣的查詢,而不是Model.connection.execute(RAW SQL STRING) (不推薦)或使用 gem activerecord-import (它於 2010 年 8 月 11 日首次發布)在這種情況下只需將數據放在數組rows調用Model.import rows

有關詳細信息,請參閱 gem 文檔

使用這個 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/ 中創建 import.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

之后在你的終端rake csv_model_import[file.csv,Name_of_the_Model]中運行這個命令rake csv_model_import[file.csv,Name_of_the_Model]

以下模塊可以在任何模型上擴展,它將根據 CSV 中定義的列標題導入數據。

筆記:

  • 這是一個很棒的內部工具,對於客戶使用,我建議添加安全措施和消毒
  • CSV 中的列名必須與 DB 模式完全相同,否則將不起作用
  • 可以通過使用表名來獲取標題與在文件中定義它們來進一步改進

在您的models/concerns文件夾中創建一個名為"csv_importer.rb"文件

module CsvImporter
  extend ActiveSupport::Concern  
  require 'csv'
  
  def convert_csv_to_book_attributes(csv_path)
    csv_rows = CSV.open(csv_path).each.to_a.compact
    columns = csv_rows[0].map(&:strip).map(&:to_sym)
    csv_rows.shift
    
    return columns, csv_rows
  end
  
  def import_by_csv(csv_path)
    columns, attributes_array = convert_csv_to_book_attributes(csv_path)
    
    message = ""
    begin
      self.import columns, attributes_array, validate: false
      message = "Import Successful."
    rescue => e
      message = e.message
    end
    
    return message
  end
end

extend CsvImporter添加到您希望將此功能extend CsvImporter到的任何模型。

在您的控制器中,您可以執行如下操作來利用此功能:

def import_file
   model_name = params[:table_name].singularize.camelize.constantize
   csv = params[:file].path
   @message = model_name.import_by_csv(csv)
end

最好使用 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"分隔

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM