LoginSignup
17
1

More than 1 year has passed since last update.

param is missing or the value is emptyの解決方法

Last updated at Posted at 2022-12-04

※ 本記事はDMM WEBCAMP mentor Advent Calendar 2022 5日目のエントリーです。

表題のparam is missing or the value is emptyのエラーなのですが、検索すると次々と見慣れない解決策(ストロングパラメータのrequire(:モデル名)の部分を消す方法)が出てくることで有名です。例えば、以下のような記事が出てきます。

これらの記事が検索上位に出てきてしまうため、初学者にとって学習の妨げになるのではないかと思い、この記事を執筆することにしました。

解決策

扱うモデル名は、ここではPostとしておきます。
コントローラに

title=posts_controller.rb
@post = Post.new

と書き、viewは

title=posts/new.html.erb
<%= form_with model: @post do |f| %>

などとしましょう。
あるいはコントローラにインスタンス変数を用意せずにviewに直接

title=posts/new.html.erb
<%= form_with model: Post.new do |f| %>

と書いても良いです。
こうしてるはずなのにエラーが出る、といった場合にはコントローラとviewで定義している変数名に間違いがないかを調べてみてください。単純にコントローラの記述が抜けていたり、複数形になってしまっていたり、モデル名によってはスペルミスがあったりするかもしれないので慎重にチェックしましょう。

なんでこれで治るの?

例えばPostモデルにtitleカラムがあったとしましょう。
前述した正しい記述の状態で、ターミナル上のログを見ると

<ActionController::Parameters {(略),"post"=>{"title"=>"5", "commit"=>"Save", "controller"=>"posts"}, "action"=>"create"} permitted: true>

のようになっているはずです。コントローラに@post = Post.newを書き忘れたりした場合には、

<ActionController::Parameters {(略),"title"=>"5", "commit"=>"Save", "controller"=>"posts", "action"=>"create"} permitted: true>

となるでしょう。
違いは"post"=>{}の中に"title"があるか、裸のまま"title"が置かれているかです。
つまり、form_withでモデルがちゃんと指定できていれば、パラメータは{ モデル => { カラム => そのカラムの値 } }のようになるということです。form_withでモデルが指定できていなければ{ カラム => そのカラムの値 }だけですね。
続いてストロングパラメータを見ましょう。

title=posts_controller.rb
params.require(:post).permit(:title)

こいつの意味は、「params(≒前述したターミナルログのパラメータ)の中からpostを探し、そのpostの中からtitleを探す。そしてそのtitleの保存を許可する」くらいの意味です。
よく挙げられるおかしな解決策(require(:post)を消す方法)と比べてみましょう。

title=posts_controller.rb
params.permit(:title)

こいつの意味はもちろん、「params(≒前述したターミナルログのパラメータ)の中からtitleを探す。そしてそのtitleの保存を許可する」です。
もうお分かりいただけたでしょうか。form_withでモデルが指定できていないとき、パラメータにモデル名が含まれていないのでrequire(:モデル名)を消すと上手くいくというのが、この解決策が蔓延っている原因です。

ここまでの説明をまとめます。

解決策1
form_withでモデルが指定されている
↓
パラメータにモデル名が含まれる
↓
require(:モデル名)が必要

解決策2(先ほど「おかしな解決策」と呼んだ方法)
form_withでモデルが指定されていない
↓
パラメータにモデル名は含まれない
↓
require(:モデル名)は消さなければならない

初学者の方はrequireを消す方法など、教材やチュートリアルで勉強していないと思うので解決策2の方は忘れて、この記事で説明した1の方の解決策を使いましょう。
また、補足項で述べますが、単に解決策1の方が優れているので初学者でなくても解決策1を使うべきです。

補足1

冒頭にあげさせていただいたおかしな解決策を提示している記事については、指摘して修正をうながすのが筋だとは思いますが、1つ目にあげさせていただいた【Rails】param is missing or the value is empty:についての著者はすでにQiitaから離れているようで、修正が見込めませんでした。一応コメントでは指摘させていただいているので、この記事を訪れた方がコメント欄まで確認してくれれば認識を正すこともできるのですが、そのような方はあまり多くないというのが僕の印象です。
他の記事の著者は最近も活動していそうなので指摘することもできますが、検索時に一番上に出てくる上記の記事と内容がほぼ同じなので、一旦は上記の記事のみコメントで指摘するのみに留めています。

補足2

記事の最後でrequire(:モデル名)を消す方法より、form_withでモデルを指定する方法が優れていると言いました。理由としては

  • Railsの機能の遍歴から推測するに

    • モデルを指定する→保存するためのデータを送るフォーム
    • モデルを指定しない→保存はしないが一時的に使うデータを送るフォーム

    として設計されているから

  • updateを実装するときに困るから

前者の方はform_forform_tagについて調べてもらうとして、後者の方は単純です。
データをupdateするにはどのデータをupdateするのかを指定する必要があり、その指定の仕方として最も簡単なのが、form_withのmodelにデータを渡すことだからです。
コントローラ側で

title=posts_controller.rb
def edit
  @post = Post.find(params[:id])
end

として、viewで

title=posts/new.html.erb
<%= form_with model: @post do |f| %>

とするだけです。(createでは@post = Post.newでした。)
createを実装する際にストロングパラメータからrequireを消してしまっていると、updateform_withにモデルを指定することが出来ずに困ります。
もちろん、モデルを指定せずにupdate機能を実装する方法もなくはないのですが、このスマートなやり方に比べると幾分も冗長です。link_toを使えばラクなところを、あえてaタグを使うようなもの、といえば分かりやすいでしょうか。

17
1
0

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
17
1