Ruby on Rails: importa i dati da un file CSV

Vorrei importare i dati da un file CSV in una tabella di database esistente. Non voglio salvare il file CSV, basta prendere i dati da esso e metterlo nella tabella esistente. Sto usando Ruby 1.9.2 e Rails 3.

Questa è la mia tabella:

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 

Puoi darmi un codice per mostrarmi il modo migliore per farlo, grazie.

 require 'csv' csv_text = File.read('...') csv = CSV.parse(csv_text, :headers => true) csv.each do |row| Moulding.create!(row.to_hash) end 

La versione più semplice della risposta di yfeldblum, che è più semplice e funziona bene anche con file di grandi dimensioni:

 require 'csv' CSV.foreach(filename, :headers => true) do |row| Moulding.create!(row.to_hash) end 

Non c’è bisogno di with_indifferent_access o symbolize_keys e non c’è bisogno di leggere prima il file in una stringa.

Non mantiene l’intero file in memoria in una sola volta, ma legge riga per riga e crea un Moulding per riga.

La gem smarter_csv stata creata appositamente per questo caso d’uso: leggere i dati dal file CSV e creare rapidamente voci di database.

  require 'smarter_csv' options = {} SmarterCSV.process('input_file.csv', options) do |chunk| chunk.each do |data_hash| Moulding.create!( data_hash ) end end 

Puoi usare l’opzione chunk_size per leggere N csv-rows alla volta, e poi usare Resque nel loop interno per generare lavori che creeranno i nuovi record, piuttosto che crearli subito – in questo modo puoi diffondere il carico di generare voci a più lavoratori.

Vedi anche: https://github.com/tilo/smarter_csv

Questo può aiutare. Ha anche esempi di codice:

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

O per un compito di rake per fare lo stesso:

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

Potresti provare 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 

Se questo è ciò che desideri, potresti anche considerare di eliminare la chiave primaria con incremento automatico dalla tabella e impostare la chiave primaria da name . In alternativa, se c’è una combinazione di attributi che formano una chiave primaria, usala come selettore. Nessun indice è necessario, lo renderà più veloce.

È meglio racchiudere il processo relativo al database all’interno di un blocco di transaction . Lo snippet blow del codice è un processo completo di seeding di un set di lingue al modello di linguaggio,

 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 

Snippet di seguito è un parziale del file languages.csv ,

 aa,Afar ab,Abkhazian af,Afrikaans ak,Akan am,Amharic ar,Arabic as,Assamese ay,Aymara az,Azerbaijani ba,Bashkir ... 

Usa questa gem: https://rubygems.org/gems/active_record_importer

 class Moulding < ActiveRecord::Base acts_as_importable end 

Quindi ora puoi usare:

 Moulding.import!(file: File.open(PATH_TO_FILE)) 

Assicurati solo che le intestazioni corrispondano ai nomi delle colonne del tuo tavolo

Il modo migliore è includerlo in un’attività rake. Creare il file import.rake in / lib / tasks / e inserire questo codice in quel file.

 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 

Dopo di che esegui questo comando nel tuo rake csv_model_import[file.csv,Name_of_the_Model] terminale rake csv_model_import[file.csv,Name_of_the_Model]

So che è una vecchia domanda, ma è ancora nei primi 10 link di Google.

Non è molto efficiente salvare le righe una ad una perché causa la chiamata al database nel ciclo ed è meglio evitarlo, soprattutto quando è necessario inserire enormi porzioni di dati.

È preferibile (e significativamente più veloce) utilizzare l’inserimento batch.

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

È ansible creare una query di questo tipo manualmente e poi fare Model.connection.execute(RAW SQL STRING) (non consigliato) o utilizzare gem activerecord-import (è stato rilasciato per la prima volta l’11 agosto 2010) in questo caso basta inserire i dati nelle rows dell’array e chiama Model.import rows

fare riferimento a documenti di gem per i dettagli

È meglio usare CSV :: Table e usare String.encode(universal_newline: true) . Converte CRLF e CR in LF

Se si desidera utilizzare SmartCSV

 all_data = SmarterCSV.process( params[:file].tempfile, { :col_sep => "\t", :row_sep => "\n" } ) 

Rappresenta i dati delimitati da tabulazioni in ogni riga "\t" con righe separate da nuove righe "\n"