IT

Ruby on Rails-CSV 파일에서 데이터 가져 오기

lottoking 2020. 5. 12. 08:12
반응형

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가 필요 없으며 파일에서 먼저 문자열을 읽을 필요가 없습니다.

전체 파일을 한 번에 메모리에 보관하지 않지만 한 줄씩 읽고 한 줄에 몰딩을 만듭니다.


smarter_csv보석은 특히이 사용 사례 만들었습니다 : 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


도움이 될 수 있습니다. 코드 예제도 있습니다.

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

또는 같은 일을하는 갈퀴 작업 :

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


시도해 볼 수 있습니다 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. 또는 기본 키를 구성하는 속성 조합이 있으면 선택기로 사용하십시오. 색인이 필요하지 않으며 더 빨라집니다.


It is better to wrap the database related process inside a transaction block. Code snippet blow is a full process of seeding a set of languages to Language model,

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 below is a partial of languages.csv file,

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

Use this gem: https://rubygems.org/gems/active_record_importer

class Moulding < ActiveRecord::Base
  acts_as_importable
end

Then you may now use:

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

Just be sure to that your headers match the column names of your table


The better way is to include it in a rake task. Create import.rake file inside /lib/tasks/ and put this code to that 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

After that run this command in your terminal rake csv_model_import[file.csv,Name_of_the_Model]


I know it's old question but it still in first 10 links in google.

It is not very efficient to save rows one-by-one because it cause database call in the loop and you better avoid that, especially when you need to insert huge portions of data.

It's better (and significantly faster) to use batch insert.

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

You can build such a query manually and than do Model.connection.execute(RAW SQL STRING) (not recomended) or use gem activerecord-import (it was first released on 11 Aug 2010) in this case just put data in array rows and call Model.import rows

refer to gem docs for details


It's better to use CSV::Table and use String.encode(universal_newline: true). It converting CRLF and CR to LF


If you want to Use SmartCSV

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

This represents tab delimited data in each row "\t" with rows separated by new lines "\n"

참고URL : https://stackoverflow.com/questions/4410794/ruby-on-rails-import-data-from-a-csv-file

반응형