1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Railsのcreateアクションではmodel.newよりcurrent_user.models.buildを使うべき理由

Last updated at Posted at 2025-06-11

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

まとめ

  • .newuser_id を自動設定しない → 手動で補完が必要
  • .build は関連を活かして自動的に user_id を設定
  • formで user_id を受け取らないためセキュリティ的にも堅牢
  • Railsの思想に沿っており、読みやすくメンテナンス性が高い
1
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?