Money Forward Developers Blog

株式会社マネーフォワード公式開発者向けブログです。技術や開発手法、イベント登壇などを発信します。サービスに関するご質問は、各サービス窓口までご連絡ください。

20230215130734

railsでseedデータを簡単に取り込む

railsエンジニアの谷口です。 csv形式のseedデータの取り込みをシンプルにした事例を紹介します。

本記事の動作環境は以下の通りです。

[code] ruby 2.1.2p95 rails 3.2.17 [/code]

db/seed_datas/ に配置したcsvをシードデータとしてデータベースに取り込みます。 例えばこんなcsvをdelete-insertしたい場合

id name
1 ほげ
2 ふが

以前は、下記のように各テーブルごとに取り込んでいました。

seeds.rb

[code language="ruby"] require 'csv'

TestTable.delete_all test_tables_csv = CSV.readlines("db/seed_datas/test_tables.csv") test_tables_csv.shift # 1行目はフィールド名なのでとばす test_tables_csv.each do |row| record = TestTable.new record.id = row[0] record.name = row[1] record.save! end [/code]     テーブルひとつひとつに対して、毎回このように記述するのは面倒ですし、事故も発生しやすいため、下記のようにすっきり記述するためのモジュールを作成しました。

seeds.rb

[code language="ruby"] include SeedModule

SeedModule.import("test_tables", delete_all: true) [/code]     モジュールの中身はこんな感じ (実際にはこの2倍くらいコードがあるんですが外部公表用に削っています。)

seed_module.rb

[code language="ruby"] require 'csv'

module SeedModule

DEFAULT_ENCODING = 'utf-8' DEFAULT_SEED_PATH = './db/seed_datas/'

@@encoding = DEFAULT_ENCODING @@seed_path = DEFAULT_SEED_PATH @@table_name = nil @@model = nil

def self.import(table_name, before_options = {}, after_options = {}, import_options = {}) @@table_name = table_name @@model = table_name.classify.constantize @@encoding = import_options[:encoding] || DEFAULT_ENCODING

exec_options(before_options, :BEFORE) if before_options.present?

import_all

exec_options(after_options, :AFTER) if after_options.present?

end

def self.exec_options(options, timing) options.each do |option, value| case option when :truncate next unless value # valueがtrueの場合のみ実行する sql = "TRUNCATE TABLE "+@@table_name+";" ActiveRecord::Base.connection.execute(sql) msg = "全てのレコードを削除:"+sql when :delete_all next unless value # valueがtrueの場合のみ実行する @@model.delete_all msg = "全てのレコードを削除" when :delete_before @@model.delete_all(["id <= " + value.to_s]) msg = "ID#{value}以下のレコードを削除" else raise "[#{@@table_name}]#{timing} : 存在しないオプション #{option}" end

  puts &quot;[#{@@table_name}]#{timing} : #{msg}&quot;
end

end private_class_method :exec_options

def self.import_all rows, fields = retrieve_rows_and_fields_from_csv

rows.each do |row|
  next if row[0].blank? # IDの列が空の行は無視する
  record = @@model.find_or_initialize_by_id(row[0])
  fields.each_with_index do |field, i|
    next if field.blank?
    record.send(field+&quot;=&quot;, row[i])
  end
  record.save!
end

puts &quot;[#{@@table_name}]Import : #{rows.size} records&quot;

end private_class_method :import_all

def self.retrieve_rows_and_fields_from_csv rows = CSV.read(@@seed_path + @@table_name + ".csv", encoding: @@encoding) # CSVの1行目からフィールド名を取得して取り除く fields = rows.shift

return [rows, fields]

end private_class_method :retrieve_rows_and_fields_from_csv

end [/code]

投稿者:谷口