【Rails】Seedsデータの具体的な書き方
機能を増やすほどseedファイルを作ることに苦戦したので、自身が作成したseedの内容を振り返りながら解説したいなと思います。
少しでもお役に立てたら幸いです。
seedとは
以前にも記事を書いていますのでここでは割愛します。
ER図
上記テーブルに基づいてseedを作ります。入れていくデータは次の通りです。
- 管理者ユーザー
- 一般ユーザー
- タグ
- 投稿(ランダムでタグ付をして、公開・下書き・非公開のステータスを分ける)
- いいね
- コメント
- フォローフォロワー
find_or_create_byメソッド
seedファイルの入れ方は、create
でなく find_or_create_by
を使用し記載していきます。
create
メソッド
create
メソッドは、単に新しいレコードを作成するだけで、既存のレコードとの重複を考慮しません。
# createメソッドの例
user = User.create(name: 'Ganmo', email: 'example@example.com')
find_or_create_by
メソッド
一方、find_or_create_by
メソッドは、指定した条件で既存のレコードを検索し、条件に一致するレコードが見つからない場合に新しいレコードを作成します。このメソッドを使用することで、データベース内での重複を避けることができます。また、新しいレコードを作成する前にバリデーションが実行されるため、バリデーションをパスしないと新しいレコードが作成されません。
# find_or_create_byメソッドの例
user = User.find_or_create_by(email: 'example@example.com') do |u|
u.name = 'Ganmo'
end
seedファイルの実際の記述
まずは挿入したseedデータです。ブロックごとに解説します。
# Adminのシードデータ
Admin.find_or_create_by!(email: ENV['ADMIN_EMAIL']) do |admin|
admin.password = ENV['ADMIN_PASSWORD']
end
# ユーザーアカウントとニックネームの配列
nicknames = ['がんも', 'だいこん', 'たまご', 'はんぺん', '牛すじ']
# ユーザー作成
def find_or_create_user(account, nickname)
introduction = "よろしくお願いします、#{nickname}です。YouTubeチャンネルを登録するとYouTubeのチャンネルアイコンに変わります。"
email = "#{account}@2seeds.com"
password = "123456"
channel = "https://www.youtube.com/channel/UCUfZCxi6GBN4fam5kiwPUnA"
status = 0
# ランダムな日付を生成
min_days_ago = 1
max_days_ago = 365
random_days_ago = rand(min_days_ago..max_days_ago)
random_date = Time.now - random_days_ago.days
user = User.find_or_create_by!(email: email) do |u|
u.account = account
u.nickname = nickname
u.password = password
u.introduction = introduction
u.channel = channel
u.status = status
u.created_at = random_date
u.updated_at = random_date
end
if user.persisted?
puts "User created successfully: #{user.account}"
else
puts "Error creating user:"
puts user.errors.full_messages
end
end
# ユーザー作成
nicknames.each_with_index do |nickname, index|
account = "2seeds#{index + 1}"
find_or_create_user(account, nickname)
end
# タグの作成
tags = %w(旅行 料理 音楽 スポーツ アート)
tags.each { |tag_name| ActsAsTaggableOn::Tag.find_or_create_by(name: tag_name) }
# 投稿の作成メソッド
def create_posts_for_user_with_ordered_dates(user, count, status_options, tags)
initial_date = Time.now - (count - 1).days
count.times do |i|
title = "投稿#{i + 1}"
body = "サンプルの投稿#{i + 1}です。新鮮な野菜、チーズ、ハムなどお好みの具材を挟んだホットサンドは、軽食からランチまで幅広く楽しめる美味しい料理です。シンプルな食材で手軽に作れ、サクサクの食感ととろけるチーズの組み合わせが絶妙です。ぜひ自宅で作ってみて、美味しいひとときを楽しんでみてください。"
link = "https://www.youtube.com/watch?v=eYreBz3S7f8"
status = status_options.sample
tag_list = tags.sample(2)
# 日付を順番に遅らせる
post_date = initial_date + i.days
post_params = {
title: title,
user_id: user.id
}
# 投稿を作成または既存のものを検索
post = Post.find_or_create_by!(post_params) do |p|
p.body = body
p.link = link
p.status = status
p.created_at = post_date
p.updated_at = post_date
p.tag_list.add(tag_list)
end
puts "Creating post with title: #{title}, user_account: #{user.account}, tag_list: #{tag_list}"
end
end
# ユーザーごとに異なるステータス割り当て
User.where.not(account: 'guest').each do |user|
statuses = [0] * 12 + [1] * 3 + [2] * 3
create_posts_for_user_with_ordered_dates(user, 12, statuses, tags) # 投稿を12個生成
end
# お気に入り投稿の作成
users = User.all
posts = Post.published
users.each do |user|
favorite_posts = posts.sample(rand(1..3))
favorite_posts.each do |post|
PostFavorite.find_or_create_by!(
user_id: user.id,
post_id: post.id
)
end
end
user_comments = [
'素敵な投稿ですね。',
'興味深い情報です。',
'共感できる内容です。',
'勉強になります。',
'面白いです!'
]
users.each do |user|
posts.each do |post|
num_comments = rand(0..1)
next if num_comments.zero?
comment_text = user_comments.sample
comment = Comment.find_or_create_by!(
user_id: user.id,
post_id: post.id
) do |c|
c.comment = comment_text
end
if rand(0..1) == 1
CommentFavorite.find_or_create_by!(
user_id: user.id,
comment_id: comment.id
)
end
end
end
# リレーションシップの作成
users.each do |user|
following_users = users - [user]
following_users.shuffle.take(rand(1..4)).each do |following_user|
Relationship.find_or_create_by!(
follower_id: user.id,
followed_id: following_user.id
)
end
end
admin
管理者作成
# Adminのシードデータ
Admin.find_or_create_by!(email: ENV['ADMIN_EMAIL']) do |admin|
admin.password = ENV['ADMIN_PASSWORD']
end
解説:
管理者の初期データの実装。
ポイントとしては、メールアドレスとパスワードを環境変数を通して、セキュリティ対策としています。
機密性の高い情報は公開されないよう注意が必要です。
user
ユーザー作成
テーブルは以下の通り。
# ユーザーアカウントとニックネームの配列
nicknames = ['がんも', 'だいこん', 'たまご', 'はんぺん', '牛すじ']
# ユーザー作成
def find_or_create_user(account, nickname)
introduction = "よろしくお願いします、#{nickname}です。YouTubeチャンネルを登録するとYouTubeのチャンネルアイコンに変わります。"
email = "#{account}@example.com"
password = "xxxxxx"
channel = "https://www.youtube.com/channel/UCUfZCxi6GBN4fam5kiwPUnA"
status = 0
# ランダムな日付を生成
min_days_ago = 1
max_days_ago = 365
random_days_ago = rand(min_days_ago..max_days_ago)
random_date = Time.now - random_days_ago.days
user = User.find_or_create_by!(email: email) do |u|
u.account = account
u.nickname = nickname
u.password = password
u.introduction = introduction
u.channel = channel
u.status = status
u.created_at = random_date
u.updated_at = random_date
end
if user.persisted?
puts "User created successfully: #{user.account}"
else
puts "Error creating user:"
puts user.errors.full_messages
end
end
# ユーザー作成
nicknames.each_with_index do |nickname, index|
account = "2seeds#{index + 1}"
find_or_create_user(account, nickname)
end
解説:
- ニックネームリストの作成
nicknames = ['がんも', 'だいこん', 'たまご', 'はんぺん', '牛すじ']
上記のコードでは、作りたいユーザーのニックネームをリストに格納しています。このリストからランダムに選ばれたニックネームが各ユーザーに割り当てられます。
- ユーザー作成関数
def find_or_create_user(account, nickname)
# ...
end
find_or_create_user
関数で、ユーザーアカウント情報を生成または検索してデータベースに保存するためのメソッドです。引数としてアカウント名、ニックネームを指定します。
- ユーザー情報生成
introduction = "よろしくお願いします、#{nickname}です。YouTubeチャンネルを登録するとYouTubeのチャンネルアイコンに変わります。"
email = "#{account}@example.com"
password = "xxxxxx"
channel = "https://www.youtube.com/channel/UCUfZCxi6GBN4fam5kiwPUnA"
status = 0
この部分では、userテーブルに基づいて各ユーザーのプロフィール情報を生成しています。
- ランダムな日付の作成
# ランダムな日付を生成
min_days_ago = 1
max_days_ago = 365
random_days_ago = rand(min_days_ago..max_days_ago)
random_date = Time.now - random_days_ago.days
サイトの仕様上、ユーザーの登録日をばらばらにしたかったのでランダムな日付になるようにしてあげます。
ここでは、最小値が1日前(昨日)から最大値を365日前(1年前)の中で設定されます。
- ユーザーアカウントの作成
user = User.find_or_create_by!(email: email) do |u|
u.account = account
u.nickname = nickname
u.password = password
u.introduction = introduction
u.channel = channel
u.status = status
u.created_at = random_date
u.updated_at = random_date
end
指定されたメールアドレスをもつユーザーアカウントをデータベースから検索します。ユーザーアカウントが見つからない場合、新しいユーザーアカウントが作成されます。
- ユーザー作成ループ
nicknames.each_with_index do |nickname, index| account = "2seeds#{index + 1}" find_or_create_user(account, nickname) end
上記のコードは、nicknames
リスト内の各ニックネームに対してループ処理を行い、ユーザー情報を作成します。このループを通じて、指定したニックネームを持つユーザーアカウントを作ります。
-
成功・エラーメッセージの表示:
if user.persisted? puts "User created successfully: #{user.account}" else puts "Error creating user:" puts user.errors.full_messages end
作成時に分かりやすいよう、ユーザーアカウントが正常に作られた場合、成功メッセージが表示されるようにします。一方、エラーが発生した場合はエラーメッセージが表示され、詳細なエラー情報が出力されます。
tags
タグ作成
# タグの作成
tags = %w(旅行 料理 音楽 スポーツ アート)
tags.each { |tag_name| ActsAsTaggableOn::Tag.find_or_create_by(name: tag_name) }
解説:
-
tags
変数に、適当なタグ名のリストを格納します。 -
tags.each { |tag_name| ... }
の部分は、tags
リスト内の各タグ名に対してループ処理を行います。 -
ループ内の
ActsAsTaggableOn::Tag.find_or_create_by(name: tag_name)
は、指定したタグ名を持つタグをデータベース内で検索します。もし該当のタグが見つからない場合、新しいタグを作成します。
posts
投稿データの作成
テーブルは以下の通り。
# 投稿の作成メソッド
def create_posts_for_user_with_ordered_dates(user, count, status_options, tags)
# 初期日付を計算します。最新の投稿が現在日付で、それ以前の投稿は日付を遡っています。
initial_date = Time.now - (count - 1).days
count.times do |i|
# 各投稿のタイトル、本文、リンク、ステータス、タグを設定します。
title = "投稿#{i + 1}"
body = "サンプルの投稿#{i + 1}です。適当な文章を入力してください。"
link = "https://www.youtube.com/watch?v=eYreBz3S7f8"
status = status_options.sample
tag_list = tags.sample(2) # ランダムなタグを2つ選択します。
# 日付を順番に遅らせます。
post_date = initial_date + i.days
post_params = {
title: title,
user_id: user.id
}
# 投稿を作成または既存のものを検索
post = Post.find_or_create_by!(post_params) do |p|
p.body = body
p.link = link
p.status = status
p.created_at = post_date
p.updated_at = post_date
p.tag_list.add(tag_list) # タグを追加します。
end
# わかりやすくするために作成した投稿情報をコンソールに表示するようにします。
puts "Creating post with title: #{title}, user_account: #{user.account}, tag_list: #{tag_list}"
end
end
# ユーザーごとに異なるステータス割り当て
User.where.not(account: 'guest').each do |user|
statuses = [0] * 12 + [1] * 3 + [2] * 3
create_posts_for_user_with_ordered_dates(user, 12, statuses, tags) # 投稿を12個生成
end
解説:
- 投稿の作成メソッド
def create_posts_for_user_with_ordered_dates(user, count, status_options, tags)
initial_date = Time.now - (count - 1).days
count.times do |i|
title = "投稿#{i + 1}"
body = "サンプルの投稿#{i + 1}です。"
link = "https://www.youtube.com/watch?v=eYreBz3S7f8"
status = status_options.sample
tag_list = tags.sample(2)
post_date = initial_date + i.days
post_params = {
title: title,
user_id: user.id
}
post = Post.find_or_create_by!(post_params) do |p|
p.body = body
p.link = link
p.status = status
p.created_at = post_date
p.updated_at = post_date
p.tag_list.add(tag_list)
end
puts "Creating post with title: #{title}, user_account: #{user.account}, tag_list: #{tag_list}"
end
end
-
initial_date
は、最新の投稿の日付を計算するために使い、最も新しい投稿は現在の日付で、それ以前の投稿は日付を遡って作り、同じ日付じゃないようにします。 -
count.times
ブロックは、指定された回数(count
回)ループします。ループごとに新しい投稿が作られます。 -
投稿のタイトル、本文、リンク、ステータス、およびタグは、それぞれのループでランダムに設定します。
-
post_params
ハッシュには、投稿を作成するための必要なパラメータが格納されています。 -
Post.find_or_create_by!
メソッドは、指定された条件に一致する投稿を検索し、見つからない場合は新しい投稿を作成します。これにより、重複する投稿が作成されるのを防ぎます。 -
生成した投稿情報は、分かりやすいよう
puts
ステートメントを使用してコンソールに出力されます。
- ユーザーごとに異なるステータスを割り当てる
User.where.not(account: 'guest').each do |user|
statuses = [0] * 12 + [1] * 3 + [2] * 3
create_posts_for_user_with_ordered_dates(user, 12, statuses, tags)
end
このブロックでは、異なるユーザーに異なるステータスを持つ投稿を生成されるようにします。
-
User.where.not(account: 'guest').each
ブロックは、"guest" アカウント以外のすべてのユーザーに対してループします。 -
statuses
配列は、各ユーザーに対して割り当てるステータスのリストです。12個のステータス0(公開)、3個のステータス1(下書き)、3個のステータス2(非公開)を生成します。 -
create_posts_for_user_with_ordered_dates
メソッドを呼び出し、各ユーザーに対して指定された数の投稿を生成します。このメソッドの引数としてユーザー、投稿数、ステータスのリスト、およびタグが渡されます。
このブロックを実行することで、各ユーザーに対して異なるステータスを持つ投稿が作られます。
post favorite
お気に入りの投稿データの作成
- お気に入り投稿の生成
users = User.all
posts = Post.published
すべてのユーザーとpublishedステータス(ステータス:0)のすべての投稿を取得します。
- ユーザーごとのお気に入り投稿の生成
users.each do |user|
favorite_posts = posts.sample(rand(1..3))
favorite_posts.each do |post|
PostFavorite.find_or_create_by!(
user_id: user.id,
post_id: post.id
)
end
end
-
users.each do |user|
ブロックは、すべてのユーザーに対してループします。 -
favorite_posts
は、ランダムに選択された1から3個の投稿を格納するための配列です。posts.sample(rand(1..3))
の部分は、posts
配列からランダムに1から3個の投稿を選択しています。
このコードを実行することで、各ユーザーがランダムなお気に入り投稿がある状態にします。
comment
コメントデータの作成
user_comments = [
'素敵な投稿ですね。',
'興味深い情報です。',
'共感できる内容です。',
'勉強になります。',
'面白いです!'
]
users.each do |user|
posts.each do |post|
num_comments = rand(0..1)
next if num_comments.zero?
comment_text = user_comments.sample
comment = Comment.find_or_create_by!(
user_id: user.id,
post_id: post.id
) do |c|
c.comment = comment_text
end
if rand(0..1) == 1
CommentFavorite.find_or_create_by!(
user_id: user.id,
comment_id: comment.id
)
end
end
end
解説:
- ユーザーコメントのリストを作成
user_comments = [
'素敵な投稿ですね。',
'興味深い情報です。',
'共感できる内容です。',
'勉強になります。',
'面白いです!'
]
コメントのリストを用意してあげます。ユーザーが投稿に対して行うコメントのテンプレートとして使用します。
- ユーザーコメントをランダムに付ける
users.each do |user|
posts.each do |post|
num_comments = rand(0..1)
next if num_comments.zero?
comment_text = user_comments.sample
comment = Comment.find_or_create_by!(
user_id: user.id,
post_id: post.id
) do |c|
c.comment = comment_text
end
各ユーザーが各投稿にランダムな数のコメントを付けるようにします。
-
users.each do |user|
とposts.each do |post|
の2つのループが、各ユーザーと各投稿に対する操作を行います。 -
num_comments = rand(0..1)
は、ランダムに0または1を選択し、それに基づいてコメントの数を決定します。0の場合、コメントを付けないためにnext if num_comments.zero?
を使用してスキップします。 -
comment_text = user_comments.sample
は、前述のコメントリストからランダムにコメントを選択します。これにより、異なるコメントが作成されます。 -
Comment.find_or_create_by!
メソッドを使用して、コメントをデータベースに保存します。
- コメントへの「いいね」を生成
if rand(0..1) == 1
CommentFavorite.find_or_create_by!(
user_id: user.id,
comment_id: comment.id
)
end
end
end
この部分は、各コメントに対して「いいね」をランダムに付けるようにしています。
-
rand(0..1) == 1
は、ランダムに0または1を選択し、1の場合にのみ「いいね」を付けるようにします。 -
CommentFavorite.find_or_create_by!
メソッドを使用して、「いいね」をデータベースに保存します。
relationships
フォローフォロワーデータの作成
# リレーションシップの作成
users.each do |user|
following_users = users - [user]
following_users.shuffle.take(rand(1..4)).each do |following_user|
Relationship.find_or_create_by!(
follower_id: user.id,
followed_id: following_user.id
)
end
end
解説:
-
users.each do |user|
ループは、各ユーザーに対して、他のユーザー(フォロワー)をランダムに選んでフォローさせることが目的です。 -
following_users = users - [user]
は、自分自身を除いた他のユーザーのリストを作成します。自分自身をフォローすることはできないため、自分以外のユーザーをフォロー対象とします。 -
following_users.shuffle.take(rand(1..4))
は、ユーザーが1人から4人の他のユーザーをランダムに選んでフォローするようにします。ランダム性を持たせることで、ユーザー間のフォロワー数が様々になります。 -
Relationship.find_or_create_by!
メソッドを使用して、ユーザー間のフォローリレーションシップをデータベースに保存します。
コマンド表
Seedファイルを実行するためのコマンドです。
シナリオ | コマンド |
---|---|
Seedファイルを実行 | rails db:seed |
データベースをリセットしSeedを実行 | rails db:reset |
本番環境でSeedを実行 | rails db:seed RAILS_ENV=production |
長くなりましたが以上です。
効果的にSeedファイルが入れられるようになるといいですよね!
読んでいただきありがとうございました。
Discussion