📚

Railsのform_withがエラーを出さない理由と、自動生成されるhiddenフィールドの正体

に公開

ご挨拶

今回、初めて技術記事を投稿します。学習の一環として得た知見を整理しつつ、同じような疑問を持つ方の参考になればと思いまとめました。
ぜひ最後まで読んでいただけると嬉しいです。

疑問に至ったきっかけ

Railsでフォームを作っているとき、モデルに存在しない属性名を使ってもなぜかエラーが出ない……そんな現象に出くわしたことはありませんか?

私自身、学習の中でこの現象に直面し、「あれ?なんでエラー出ないの?」と不思議に思ったことがありました。
そこで今回は、この仕組みがなぜ成り立つのかを深掘りしてみた内容を共有したいと思います。

現象:属性がないのにフォームが動く?

例えば以下のように form_with を使って、存在しない属性 image_position を指定してみます。

<%= form_with model: @post do |f| %>
  <%= f.select :image_position, options_for_select(["left", "center", "right"]) %>
<% end %>

このとき、@post モデルに image_position カラムがなくても、Railsはフォームの構築を止めることはありません。
代わりに、空の <input type="hidden"> を生成してその場をやり過ごしてくれるのです。

なぜそんなことが起こるのか?

これは、RailsのFormBuilderが「この属性は仮想属性なのでは?もしくは後から追加される可能性があるのでは?」と察して、エラーを出さない設計になっているからです。

hiddenフィールドの正体

指定した属性がモデルに存在しないと、form_with はその入力要素を適切に扱えないため、代わりに「空の hidden フィールド」を自動生成します。
これはフォームの構文上の整合性を保つための工夫です。

実際にブラウザで検証すると、次のようなHTMLが生成されていることが確認できます。

<input type="hidden" name="post[image_position]" id="post_image_position" />

これは、Railsが「現時点では使えない属性だけど、後で必要になるかも?」と判断して黙ってフィールドを用意してくれている状態です。

この設計のメリット

  • 仮想属性(Virtual Attributes) の利用が可能
    • 実際のDBにカラムを持たないが、一時的に使いたい属性
    • 例:password_confirmation, terms_of_service など
  • フロントエンドとバックエンドの並行開発
    • フォームだけ先に作成し、バックエンドは後から対応でもOK
  • UIプロトタイピングがしやすい
    • 「この項目を追加するとどう見えるか?」を即座に確認できる

FormBuilderとは?

form_withdo |f| に渡される f は、FormBuilder という特別なオブジェクトです。
これを使うことで、モデルとフォーム要素を紐づけた入力項目を簡潔に生成できます。

<%= form_with model: @post do |f| %>
  <%= f.text_field :title %>
  <%= f.select :image_position, ["left", "center", "right"] %>
<% end %>

f を通して生成された要素は、Railsが自動的に name, id, value を補完して、整ったHTMLを生成してくれます。

注意点:エラーが出ない≠問題がない

一見うまく動いているように見えても、実際には値が送られていないというケースは十分にあり得ます。
開発中に意図通りのデータが渡っていないと感じたら、まず属性名を見直すことが肝心です。

まとめ

Railsの form_with は、開発者の書いたフォーム構造を尊重し、たとえまだ存在しない属性であっても優しく受け入れてくれます。
これはとても便利な仕様ですが、その裏で「本当にそのカラム、存在してる?」という確認は怠らないようにしましょう。

しっかりバリデーションと動作確認をしていくことが、バグの少ない実装につながります。
最後まで読んでいただき、ありがとうございました。

使用技術

この記事は以下の環境で確認しました:

  • Ruby 3.2.3
  • Rails 7.1.2
  • 開発環境:Docker(macOS)

Discussion