kame's engineer note

技術関連のメモをブログに記録していく

seedについてまとめてみる

seeds関連ファイルの階層化

初期の状態だとdb/seeds.rbだけを読み込んで実行するいという流れだが、テーブル数や登録するレコード数が多かったりすることで行数が長くなり、管理がしにくくなってしまう。
そのため、下記のようにRailsの環境で階層化し、その中に各テーブルごとにファイルを配置するような構成にした。

例:
usersテーブル
postsテーブル
commentsテーブル

  • db
    • seeds.rb
    • seeds
      • production
      • test
      • development
        • users.rb
        • posts.rb
        • comments.rb

db/seeds.rb

table_names = %w(users posts comments)

table_names.each do |table_name|
    path = Rails.root.join("db/seeds",Rails.env,table_name + ".rb")
    if File.exist?(path)
        puts "Creating records for #{table_name}"
        require path
    end
end

流れとしては、seeds.rbでseedsディレクトリ配下にある各テーブルのrubyファイルを読み込んで、その中に書かれているsqlを実行していく。
最初に配列であるtable_namesにテーブル名を格納してeach文でテーブル名と一致するrubyファイルのパスを取得し、requireで読み込む。

各テーブルのrubyファイルにはcreateでレコード挿入のコードを記述する。

db/seeds/development/posts.rb

Posts.create(
    title: "test",
    entry: "this is a test entry.",
    publish_date:Date.today
)

BULK INSERTを使って高速化

テストする際はレコードを大量に挿入したいという状況が多いかと思う。
for文やらwhile文で一回一回insertを実行してもいいのだが、MYSQLには大量データを一回のinsertで挿入することができるBULK INSERTという機能がある。
そこで、このBULK INSERTactiverecordで実行できるようにするactiverecord-importというGemを使う。
insertよりもかなりパフォーマンス向上に繋がるということ。

参考URL
ActiveRecordで複数レコード、BULK INSERTする方法とパフォーマンスについて

Gemfile

gem 'activerecord-import'

使い方は下記のようにモデルのインスタンスを一旦配列に格納しておき、importメソッドBULK INSERTを実行するという流れである。

arr = []
1.upto(100) do |i|
    arr << Post.new(
        title:"test#{i}",
        entry: "this is a test#{i} entry.",
        publish_date:Date.today + eval("#{i}.days")
    )
end
Post.import arr

レコードとオートインクリメントの初期化

毎回手動でレコードやオートインクリメントを初期化するのは面倒だ。
レコードをinsertする前に下記の記述で初期化しようとしたが、オートインクリメントの初期化が何故かうまくいかない。。

Post.delete_all
Post.connection.execute("ALTER TABLE posts AUTO_INCREMENT = 0;")

そこでTRUNCATEコマンドを使ってみる。
上記はレコードを全て削除してオートインクリメントを初期化する方法だが、TRUNCATEは一旦テーブルを削除して,再度作りなおすコマンド。
こちらのほうがスッキリしてるし、ちゃんと動作もするのでこちらを採用した。

Post.connection.execute("TRUNCATE TABLE posts;")

最後に

seedコマンドを実行する。

bundle exec rake db:seed
Creating records for users
Creating records for posts
Creating records for comments

deviseを使う場合

  • 単純にpasswordに文字列を指定するだけで、暗号化してくれる。
  • Deviseの登録時の仕様上、BULK INSERTは使えないので素直にcreate

db/seeds/development/users.rb

1.upto(100) do |i|
    User.create(
        username: "yamada#{i}",
        email: "yamada#{i}@example.com",
        password: "password",
    )
end