Railsで初期データを投入する時、rake db:seed
を叩くと思う。色んな gem や tips を組み合わせることで幸せになれたので、ここにまとめておく。
なお、ここに書いてある内容は、参考ページに書いてある内容がほとんどであり、色々と集約したものになっている。原文記事の作者様には大変感謝である。
TL;DR
- 以下の gem を使うと
rake db:seed
が捗るようになった
- stympy/faker : ダミーデータ生成(色々)
- willnet/gimei : ダミーデータ生成(日本語 姓名、住所)
- brainspec/enumerize : Enum 定義関連
- zdennis/activerecord-import : バルクインサート/バルクアップデート
- ついでに
rake db:seed:user
みたいにできるようにしたらdb/seeds.rb
がスッキリした
前提
Rails歴が浅いので他にベストプラクティスがあるかも。その場合はぜひマサカリコメントして欲しい。
環境
- ruby 2.3.1
- Rails 4.2.7.1(db : mysql)
- faker 1.6.6
- gimei 0.2.0
- enumerize 2.0.1
- activerecord-import 0.17.1
やったこと
まずはgemをインストール
gem 'enumerize'
gem 'activerecord-import'
group :development, :test do
gem 'faker'
gem 'gimei'
end
faker の日本語ロケールファイルを取得
stympy/faker : faker/lib/locales
上記ページに ja.yml があるので、ダウンロードして lib/locales/ja.yml
に配置。
新規 rake タスクを作成
lib/tasks/seed.rake
作成
vi lib/tasks/seed.rake
などで lib/tasks/seed.rake
を作成。
+ Dir.glob(File.join(Rails.root, 'db', 'seeds', '*.rb')).each do |file|
+ desc "Load the seed data from db/seeds/#{File.basename(file)}."
+ task "db:seed:#{File.basename(file).gsub(/\..+$/, '')}" => :environment do
+ load(file)
+ end
+ end
続いて vi db/seeds.rb
で db/seeds.rb
を編集。
+ Dir.glob(File.join(Rails.root, 'db', 'seeds', '*.rb')) do |file|
+ load(file)
+ end
※コメントで、もっとシンプルな書き方を教えて頂いたので、そちらもあわせてご確認ください。
db/seeds
ディレクトリ作成
$ mkdir db/seeds
rake タスクの確認
まずはそのまま rake -T db:seed
を打ってみる。
$ rake -T db:seed
rake db:seed # Load the seed data from db/seeds.rb
次に db/seeds/hoge.rb
を作ってからもう一度打ってみる。rake db:seed:hoge
が新しくできているのが分かる。
$ touch db/seeds/hoge.rb
$ rake -T db:seed
rake db:seed # Load the seed data from db/seeds.rb
rake db:seed:hoge # Load the seed data from db/seeds/hoge.rb
試しに db/seeds/hoge.rb
を編集してから rake db:seed:hoge
と打ってみる。
$ echo "p 'hoge'" >> db/seeds/hoge.rb
$ cat db/seeds/hoge.rb
p 'hoge'
$ rake db:seed:hoge
"hoge"
同様に db/seeds/fuga.rb
を作ってから rake db:seed:fuga
および rake db:seed
を打ってみる。rake db:seed:fuga
は db/seeds/fuga.rb
だけが、 rake db:seed
は db/seeds/*.rb
が実行されているのが分かる。
$ echo "p 'fuga'" >> db/seeds/fuga.rb
$ cat db/seeds/fuga.rb
p 'fuga'
$ rake db:seed:fuga
"fuga"
$ rake db:seed
"hoge"
"fuga"
これでモデル毎にファイルを分ければ、不要なデータは投入しなくて良いなど、自由度が上がる。
ダミーデータの投入
実際に作ったファイル
まずは自分が作った db/seeds/user.rb
の内容を晒してみる。User
モデルにありがちなデータを投入していると思って見て欲しい。
# coding: utf-8
# Create dummy users
# Male
users = []
50.times do |no|
gimei = Gimei.male
users << User.new(
user_name: Faker::Internet.user_name,
email: Faker::Internet.email,
first_name: gimei.first.kanji,
first_name_kana: gimei.first.hiragana,
last_name: gimei.last.kanji,
last_name_kana: gimei.last.hiragana,
birthday: Faker::Time.between(40.years.ago, 18.years.ago, :all).to_s[0, 10],
gender: User.gender.male.value,
univ_id: University.pluck(:id).sample,
)
end
# Female
50.times do |no|
gimei = Gimei.female
users << User.new(
user_name: Faker::Internet.user_name,
email: Faker::Internet.email,
first_name: gimei.first.kanji,
first_name_kana: gimei.first.hiragana,
last_name: gimei.last.kanji,
last_name_kana: gimei.last.hiragana,
birthday: Faker::Time.between(40.years.ago, 18.years.ago, :all).to_s[0, 10],
gender: User.gender.female.value,
univ_id: University.pluck(:id).sample,
)
end
# Bulk insert
User.import users
ざっくりと解説
コードそのままだが、流れとしては、
- バルクインサートする用の配列変数
users
を初期化 - 50人分の男性データを
users
に追加 - 50人分の女性データを
users
に追加 - 100人分のユーザーデータを
User.import users
でバルクインサート
となっている。
activerecord-import の活用
もうお分かりの通り、
- 配列の初期化
- 配列にデータを追加していく
- 最後に
Hoge.import hoges
だけで良い。SQLが1回で済む分、毎回 User.create
とするより実行速度が早い。
Faker, Gimei の活用
今回は使ってないものもあるが、使えそう(良く使いそう)なものを簡単にまとめておく。
対象データ | 利用メソッド | サンプル | 備考 |
---|---|---|---|
ユーザー名 | Faker::Internet.user_name |
ollie |
|
メールアドレス | Faker::Internet.email |
nathen@turcotte.org |
|
姓名(漢字) | Gimei.kanji |
石田 弥安 |
※1 |
姓名(ひらがな) | Gimei.hiragana |
もりた たいさく |
※1 |
姓名(カタカナ) | Gimei.katakana |
ホリエ ケイスケ |
※1 |
名(漢字) | Gimei.first.kanji |
昇平 |
※1 |
名(ひらがな) | Gimei.first.hiragana |
ひろえ |
※1 |
名(カタカナ) | Gimei.first.katakana |
イクフミ |
※1 |
姓(漢字) | Gimei.last.kanji |
石田 |
※1 |
姓(ひらがな) | Gimei.last.hiragana |
たにがわ |
※1 |
姓(カタカナ) | Gimei.last.katakana |
タナカ |
※1 |
日付 | Faker::Time.between(40.years.ago, 18.years.ago, :all).to_s[0, 10] |
1977-08-04 |
※2 |
時刻 | Faker::Time.between(40.years.ago, 18.years.ago, :all).to_s[11, 8] |
23:44:20 |
※2 |
住所(都道府県) | Faker::Address.state |
山形県 |
|
住所(都道府県:漢字) | Gimei.prefecture.kanji |
宮崎県 |
※1 |
住所(都道府県:ひらがな) | Gimei.prefecture.hiragana |
ぎふけん |
※1 |
住所(都道府県:カタカナ) | Gimei.prefecture.katakana |
トットリケン |
※1 |
住所(市?) | Faker::Address.city |
奈々区 |
|
住所(市:漢字) | Gimei.city.kanji |
長岡市 |
※1 |
住所(市:ひらがな) | Gimei.city.hiragana |
いさはやし |
※1 |
住所(市:カタカナ) | Gimei.city.katakana |
ヤマモトグンフジサトマチ |
※1 |
住所(町:漢字) | Gimei.town.kanji |
黒木町北大淵 |
※1 |
住所(町:ひらがな) | Gimei.town.hiragana |
いいじまみちひがし |
※1 |
住所(町:カタカナ) | Gimei.town.katakana |
カメオチョウ |
※1 |
※1
Gimei.kanji
と毎回打つと値が変わるので、Gimei.kanji
Gimei.hiragana
Gimei.katakana
はバラバラになってしまう。同じ名前の情報が欲しい場合は gimei = Gimei.new
や gimei = Gimei.male
gimei = Gimei.female
としてから、 gimei.kanji
gimei.hiragana
gimei.katakana
としてやれば良い。
今回自分が作ったものも、あらかじめ gimei = Gimei.male
としてからデータを取るようにしている。
※2
DB のフォーマットに合わせて取り出す良い方法が見付からなかったので力技。誰か良い方法を知ってれば教えて欲しい。
※3
Faker は ja.yml
を編集していけば、全てのデータが日本語化できると思われる。公式ロケールファイルは情報が少ないので、適宜カスタマイズしていくと良い。(ついでにプルリクしてあげると喜ばれそう)
※4
細かいリファレンスはそれぞれ以下を参照して欲しい。
- willnet/gimei : 使い方
- stympy/faker : Faker::Address
- stympy/faker : Faker::Internet
- stympy/faker : Faker::Name
- stympy/faker : Faker::Time
- stympy/faker : Faker::Date
Enumerize の活用
モデルに Enumerize
を使うことを追加。細かい部分は後述の参考ページを見て欲しい。
class User < ActiveRecord::Base
extend Enumerize
enumerize :gender, in: {:male => 0, :female => 1}, default: :male, predicates: true, scope: true
i18n 対応。(今回の話とは関係無し)
ja:
enumerize:
user:
gender:
male: 男性
female: 女性
Enumerize
の定義を追加すると、User.gender.male.value
User.gender.female.value
などで値を取得できる。直接 0
1
と書くより可読性が上がる。
参考にしたページ
Faker/Gimei
余談だが、以下の記事の通り Faker v1.7.3 でユニーク値の生成(重複の回避)がサポートされたとのこと
Enumerize
- Enumerizeを数値型カラムで使う - Qiita
- Ruby (on Rails) で使える enumeration 実装を比較してみた - blog.kymmt.com
- RailsでEnumerizeの使い方 | Gemの紹介 | DoRuby
- [Ruby on Rails] enumerizeの使い方 | Gemの紹介 | DoRuby
activerecord-import
- Railsでバルクインサート・アップデート - Qiita
- (Rails) BULK INSERT、BULK UPDATEの実装方法 — ALL-IN Tech Blog
- 大量のデータ挿入・更新にバルクインサート・バックアップデートを利用する - 49hack
- Rails(ActiveRecord)でBULK INSERTする方法 - kurobaraのブログ
rake db:seed
その他見たけど採用しなかったものたち
好き嫌いとかプロジェクトの方針とかあるので、人によってはこっちの方が良いかもと思い、全部載せておきます。
-
railsで初期データを入れる(seed-fuの使い方) - Qiita
mbleigh/seed-fu よりも前述のやり方の方が肌に合いそうだったので採用見送り。その内、一回試してみたい。 -
seedファイル名を指定して rake db:seed - u16suzuの blog
rake db:seed_from_file SEED_FILENAME='hogehoge.rb'
よりrake db:seed:hoge
の方が直感的かなと。 -
FakerとFFakerの比較 - Qiita
ffaker/ffaker の存在は(たぶんこの記事見て)知って試したのですが、日本語化周りとかでうまくいかずに不採用にした(はず、昔のことなので忘れた…)。 -
Fakerでダミーデータを作成 - Qiita
この記事書いてる時に tily/ruby-faker-japanese があるのに気付いた。ただ、最終コミットが6年前なので、willnet/gimei で結果的に正解だったかな。