目的
- Qiitaの記事に大まかな流れをテンプレート化しておき、作業効率を上げる。
- 今後、railsを学ぶ方に向けての参考に役立てる。
前提条件
-
実行環境
- Ruby 2.5.1
- Rails 5.2.3
- MySQL 5.7
-
gem
-
想定
- 大量のデータ(CSVファイル)をseedで一括に取り込みたい
手順
- CSVファイルを設置する
- seeds.rbにCSVをBULK INSERTするコードを記述
- 各modelにオプションを設定
-
rails db:seed
(完)
1. CSVファイルを設置する
db
├── data
│ ├── hoge.csv ← 初期データとして取込みたいCSVファイルを設置
│ └── fuga.csv ← 初期データとして取込みたいCSVファイルを設置
├── migrate
│ ├── 20191118191522_create_huges.rb
│ └── 20191119060537_create_fugas.rb
├── schema.rb
└── seeds.rb
2. seeds.rbにCSVをBULK INSERTするコードを記述
db/data/seeds.rb
# csvライブラリを読み込みます。
require 'csv'
ActiveRecord::Base.transaction do
# Procオブジェクト
# ブロックの部分だけを先に定義して変数に代入しておき、後からブロック付きメソッドを渡す
import = ->(klass, file_path) do
options = {
encoding: "UTF-8",
headers: true,
# header_converters: Array
# 値を変換する前に UTF-8 にエンコーディング変換を試みます
# エンコーディング変換に失敗した場合はヘッダは変換されません。
header_converters: klass.csv_header_converters,
# converters: Hash
converters: klass.csv_converters
}
# 登録件数が多いことを想定して念のため1000件ずつ処理を行うように設定
CSV.read(file_path, options).each_slice(1000) do |rows|
values = rows.map(&:to_h).map { |row| klass.new(row) }
klass.import! values, validate: true
end
end
import.(Hoge, Rails.root.join('db', 'data', 'hoge.csv'))
import.(Fuga, Rails.root.join('db', 'data', 'fuga.csv'))
end
3. 各modelにオプションを設定
app/models/hoge.rb
class Hoge < ApplicationRecord
belongs_to :fuga
validates :label, :fuga_id, :year, :month, presence: true
class << self
def csv_header_converters
headers = {
ID: :id,
Firstname: :firstname,
Lastname: :lastname,
City: :city,
Num_Of_People: :num_of_people,
Has_Child: :has_child
}
# 引数nameにはHashのkeyが入る
-> (name) {
headers[name] || raise
}
end
def csv_converters
nil
end
end
end
app/models/fuga.rb
class Fuga < ApplicationRecord
has_many :hoge
validates :firstname, :lastname, :city, presence: true
validates :has_child, inclusion: { in: [true, false] }
class << self
def csv_header_converters
headers = {
ID: :id,
Firstname: :firstname,
Lastname: :lastname,
City: :city,
Num_Of_People: :num_of_people,
Has_Child: :has_child
}
# 引数nameにはHashのkeyが入る
-> (name) {
headers[name] || raise
}
end
def csv_converters
-> (field, field_info) {
if field_info.header == :has_child
field == 'Yes'
else
field
end
}
end
end
end
4. rails db:seed
(完) 👍
bash
rails db:seed
関連URL
Ruby標準添付CSVライブラリのCSV::Convertersについて調べてみた
CSV のヘッダー行の日本語をシンボルに変換して要素にアクセスしたりとか
メソッド呼び出し(super・ブロック付き・yield)
手続きオブジェクトの挙動の詳細