##はじめに
こんにちは!
DMM WEBCAMP Advent Calendar 2021🎄 11日目を担当させていただきます!
普段DMMでメンターをさせていただいております、@keijitsumoriです。
この記事では、Railsを始めた頃、躓きがちなタイトルのエラーについて、考えられる原因をあげ、その一つについて、RailsのフォームとModel.newの解説を挟みながら、なぜエラーになってしまうのかを、自分なりに説明できればと考えております。
このエラーをきっかけに、Railsの見えていなかった部分が自分も見えるようになりました。
ご指摘等いただけますと幸いです。
この記事の対象者
- param is missing or the value is empty errorが解決できない。
- param is missing or the value is empty errorが解決できたが、理由がわからない。
この記事でわかること
- 上記のエラーについて、自分が体験したことのある原因を紹介し、エラーの裏で何が起こっているのか、お話しします。
エラーの意味
解決策の前に、このエラーの意味について解説します。問題解決の前には、なんとなく当たりをつけて、適当にいろんな場所を変更するのではなく、エラーの意味から仮説を立て、解決するようにしましょう。
少しだけ前提知識を挟みます。
Railsでは、フォームから入力された値はパラメータとして作成したビューのフォーム通し、ブラウザからサーバーに送られます。 フォームの値は一度Railsによって梱包され、その後、処理して欲しいアクションに送られている、という感じです。
そのパラメータがこちら。
Parameters: {"utf8"=>"✓", "authenticity_token"=>"省略...", "model"=>{"title"=>"test2", "body"=>"test2"}, "commit"=>"Create Model" }
内容は置いておいて、上のような形で送られています。エラー画面や、ターミナルで見たことありませんか?
Railsではこのパラメータという形で値が運ばれているのです。
イメージはこんな感じです。
これを念頭に置いて、まずは、エラーの日本語訳から
「パラメータがないか、値が空です」
ということで、このパラメータというのが、まさに先ほど紹介したもののことです。 このパラメータの中身がない、もしくは値が空になっているという意味だったのです。 よって、このパラメータを作成しているフォーム、もしくはこのパラメータを受け取っているparamsに原因があるのではないかと推察できます。
考えられるエラーの原因
エラー文の内容を解読したので、次はその内容から具体的に何が悪いのか考えていきます。
- formがうまく作成されていない。
- formがModel.newを受け取れていない。
エラー文から、パラメータの生成か、その受け取りに原因があると考えられます。ここまでの流れからも分かる通りRailsは、実は丁寧にエラーの原因の箇所を提案してくれています。
※前提としてですが、ここに書いたもの以外の原因ももちろん考えられます。参考程度にお考えください。
解決策
ここでやっと本題の解決に乗り出します。
####formがうまく作成されていない
form_withのformがうまく適応できいるか、確認してみてください。ただし、これが原因で、このエラーになることはあまりなく、別のエラーが出る。もしくは、エラーが出ず、投稿だけできないということが多いように思います。実際投稿できない原因にはなり得るので、書きました。
誤字脱字のチェック、フォームの作成方法の復習などしてみてください。
以下参考になるサイトです。
フォームの作り方
フォームの作り方2
フォームのソースコード
####formがModel.newを受け取れていないの解決
ここの内容のためにこの記事を書きました。
まず具体的な解決策です。
<%= form_with model: Model.new do |f| %>
上記のようにform_with modelにモデル名.newを渡してあげてください。
もしくは、
def view
@model = Model.new
end
<%= form_with model: @model do |f| %>
コントローラー内で定義したものをモデルに渡してあげてください。
※そのままの文字ではなく、ご自身のアプリケーションに沿った命名で実装してください。
以下詳細です。
####form_withについて
form_withは結局のところ何をしているのかということですが、先ほどのパラメータを作り、中に値を入れてサーバーのリクエストしたいアクションに送ってくれています。
値を包んで配達しているイメージですね。
大事なのはここからで、その時、form_withにModel.newを渡すか否かで、パラメータの形式が変わるのです。これが後々、エラーの原因となってきます。
先ほどのパラメータを参考に具体例を示します。
form_withのmodelにModel.newが設定されている場合
Parameters: {"utf8"=>"✓", "authenticity_token"=>"省略...","model"=>{"title"=>"test", "body"=>"test"}, "commit"=>"Create Model" }
イメージはこんな感じです。
form_withにModel.newが渡せていない場合
Parameters: {"utf8"=>"✓", "authenticity_token"=>"省略...", "title"=>"test", "body"=>"test", "commit"=>"Create Model"}
イメージはこんな感じです。
違いは一目瞭然ではないでしょうか。Model.newを渡すと、modelというものの中に値がもう一段階ネストして格納されています。
この違いを一旦おぼえていただいたまま、話をこのパラメータを受け取る部分にうつします。
実は、このエラー、民間療法のような解決策が検索すると出てきてしまいます。
それが require(:model)を削除する とうまくいくというものなのですが、この記事の前に参考にされたりしていませんか? 話が少しそれていますが、大事なので、簡潔に。
このエラーの解決方法は、今後、paramsを使って情報を受け取り、updateなどをする際にエラーの原因となってしまいますので、お勧めできません。
なぜ、これで解決できてしまうのか、というのが上でお話しした、二つのパターンのパラメータとストロングパラメータの値の探し方に関連しています。
話を戻して、受取手のストロングパラメータのことです。
####ストロングパラメータについて
def model_params
params.require(:model).permit(:title, :body)
end
送られた値を処理するコントローラに定義されているストロングパラメータについてです。
このparamsは先ほどパラメータの中から該当の値を選別し、取り出すという働きをしています。
このコードを日本語にしてみると、
「送られてきたパラメータの中から、modelという項目の中の、title、bodyに当てはまるものを取り出す」
といった感じでしょうか。
まず送られてパラメータがparamsとして受け取られています。
そして
1.requireの文でmodelという箱が探され
2.permitの文でその中からさらにtitle, bodyが探されているのです。
察しのいい方はお気づきかもしれませんが、先ほど確認していただいた、パラメータの変化と、このrequireがあるのか、ないのかというのは対応しています。
前提知識が揃ったので、解決策として、具体的にいきます。
####エラーが消える二つのパターン
投稿が成功するパターンはform_withにModel.newを渡していて、require(:model)もあるパターン。
このパターンの時のパラメータは以下のようです。
Parameters: {"utf8"=>"✓", "authenticity_token"=>"省略...","model"=>{"title"=>"test", "body"=>"test"}, "commit"=>"Create Model" }
このパターンの時のparamsは以下のようです。
def model_params
params.require(:model).permit(:title, :body)
end
Model.newを渡すことで、titleとbodyがmodelでくくられています。paramsにはrequireがついているので、modelの中から該当のtitleとbodyを探します。
探し方は以下のようです。
もう一つ成功するパターンはform_withにModel.newは渡さず、さらにrequireを削除するパターン。
その際のパラメータは以下のようです。
Parameters: {"utf8"=>"✓", "authenticity_token"=>"省略...", "title"=>"test", "body"=>"test", "commit"=>"Create Model"}
その時のparamsは以下のようです。
def model_params
params.permit(:title, :body)
end
Model.newを渡していませんので、title, bodyがくくられず、パラメータに格納されています。そして、とりだすparamsには、requireがありませんので、title, bodyを探しにいきます。パラメータは上記の通り、modelでくくられてはいないので、値まではうまく辿り着けるのです。
※ただしこの解決方法は、上記の通り非推奨です。
このように、パラメータの形と、探し方が一致すると、Railsが値を発見し、エラーとならないのです。
##まとめ
解決策はform_withにModel.newを渡してあげることです。
自分なりにこのエラーを見て改めて思ったのは、エラーは理解につながるというものです。なんとなくでできてしまったものは学びには繋がりにくいです。皆さんも、ぜひ、なんとなくではなく、理解してエラーを解決してください。
また、Railsは本当によくできたフレームワークでたくさんのことを代わりにやってくれています。今回のように、ご自身でも公式リファレンスや、ソースコードを通して、内部をのぞいてみて下さい。
この記事が皆さんの参考になれば、それ以上のことはありません。
自分の拙い記事を最後まで読んでいただきありがとうございました。
ご指摘等、何卒、お願いいたします。
また、この記事を書くにあたり@SawaShuyaさんにアドバイスをいただきました。本当にありがとうございました。
###参考