Railsでオブジェクトを作成する際、次の2つの書き方がよく見られます:
# A: 一般的な生成方法
@post = Post.new(post_params)
@post.user_id = current_user.id
# B: 関連付けを活用した生成方法
@post = current_user.posts.build(post_params)
結論から言えば、Userが所有するモデル(記事・投稿・コメントなど)を作成する場合はBのcurrent_user.posts.build
を使った方がいいです。これは記事(Post)に限らず、Userに属する任意のモデルに対して言えます。
なぜ .new
より .build
を使うのか?
それぞれの挙動の違い
方法 | user_id の設定 | 特徴 |
---|---|---|
Post.new(post_params) |
自動設定されない(手動で設定が必要) | 単体でオブジェクトを生成する |
current_user.posts.build(post_params) |
user_id が自動で設定される |
親モデル(User)との関連付けを活用 |
.build
のメリット
1. user_id を明示的に書かなくてよい
# わざわざこう書かなくて済む
@post = Post.new(post_params)
@post.user_id = current_user.id
→ .build
を使えば次の1行で済みます:
@post = current_user.posts.build(post_params)
2. ActiveRecord の関連を活かせる
Rails では、モデル間の関連(has_many / belongs_to)を記述しておけば、以下のように親モデル経由で子モデルを自然に生成できます。
class User < ApplicationRecord
has_many :posts
end
class Post < ApplicationRecord
belongs_to :user
end
この設計思想に沿って書くと、コードが 意図の見える、Railsらしい記述 になります。
3. セキュリティと責任の明確化
formから user_id
を送信させるのではなく、controller側でログイン中のユーザーに関連づけることで、意図しないデータの紐付けを防げます。
セキュリティ観点:formから user_id
を渡す危険性
フォームに user_id
を含めると、悪意のあるユーザーが別の user_id
を指定して送信することも可能になります。これは権限昇格(Privilege Escalation)の典型例です。
<!-- 悪意ある改ざんが可能なform例 -->
<input type="hidden" name="post[user_id]" value="999">
これにより、別のユーザーに属する投稿が作られるリスクがあります。
一方、コントローラーで .build
を使えば、current_user
に紐づけられるため、このようなリスクを構造的に排除できます。
Railsらしさ:一貫性ある設計思想
Railsの設計思想は明示的よりも宣言的、繰り返しよりもDRY、意図の明確化といった原則に基づいています。
current_user.posts.build
という書き方は、
- 誰が投稿を作っているのか
- その投稿がどのユーザーに属しているのか
といった情報をコードの見た目で自然に伝えられます。これは読みやすさ・保守性・バグの少なさに直結します。
アンチパターンとの比較
NG例:.new
+ user_id
手動設定
@post = Post.new(post_params)
@post.user_id = current_user.id
デメリット:
-
user_id
の設定漏れのリスク - テストやリファクタ時に誤って外れる可能性
- セキュリティ的に甘くなる(formに
user_id
を入れたくなる)
構造的に脆いコードになります。
使用例:Controller の create アクション
class PostsController < ApplicationController
def create
@post = current_user.posts.build(post_params)
if @post.save
redirect_to @post, notice: "投稿が作成されました。"
else
render :new, status: :unprocessable_entity
end
end
private
def post_params
params.require(:post).permit(:title, :body)
end
end
まとめ
-
.new
はuser_id
を自動設定しない → 手動で補完が必要 -
.build
は関連を活かして自動的にuser_id
を設定 - formで
user_id
を受け取らないためセキュリティ的にも堅牢 - Railsの思想に沿っており、読みやすくメンテナンス性が高い