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備忘録】Railsフォーム実装の「インスタンス変数」と「直接モデル参照」使い分け・バリデーションエラーの値保持

Posted at

目次

  1. はじめに
  2. インスタンス変数と直接モデル参照の違い
    • 2.1 インスタンス変数を使う場合
    • 2.2 直接モデル参照を使う場合
    • 2.3 それぞれのメリット・デメリット
    • 2.4 コードの記述方法とポイント
    • 2.5 DBモデルの場合の注意
  3. バリデーションエラー時の値・エラー保持とリダイレクト
    • 3.1 render :new の場合
    • 3.2 redirect_to new_path で値・エラーを保持する方法
    • 3.3 コード例と解説
    • 3.4 renderとredirect_toの違い
    • 3.5 セッション利用の注意点
  4. エラーメッセージの日本語化
    • 4.1 ja.ymlの設定
    • 4.2 テストコードの修正
    • 4.3 I18n設定の確認
  5. テストコードの記述例と注意点
    • 5.1 直接モデル参照の場合
    • 5.2 インスタンス変数を使う場合
  6. まとめ

1. はじめに

昨日インスタンス変数に関することを投稿しておきながら、本日作業中にうまく思っているとおりに実装ができず、かなり長時間躓いた結果、頭がパンクしてしまったので、再度情報整理と、本日行った作業を言語化します(完全に自分の備忘録、わからなくなる自信ある)。

Railsでフォームを実装した際、「インスタンス変数でデータを渡す」か「モデルを直接参照する」か迷い、いざインスタンス変数でデータ渡してたのに、テストコードのこと考慮してなくてエラーが出て頭を抱えたりしていました。
また、バリデーションエラー時の値・エラーの保持や日本語化も、いくつかバリデーションの設定箇所があったりするので、実装やテストでつまずきやすいポイントだと感じました。


2. インスタンス変数と直接モデル参照の違い

2.1 インスタンス変数を使う場合

コード例

コントローラー

def new
  @item = Item.new
  @categories = Category.where.not(id: 1)
end

ビュー

<%= f.collection_select(:category_id, @categories, :id, :name, {prompt: '---'}) %>

解説

  • コントローラーでインスタンス変数(@categories)にデータをセットし、ビューでそれを参照させる
  • DBから取得したデータや、複雑な条件で加工したデータを渡したい場合に便利

2.2 直接モデル参照を使う場合

コード例

ビュー

<%= f.collection_select(:category_id, Category.where.not(id: 1), :id, :name, {prompt: '---'}) %>

解説

  • ビューで直接モデルのクラスメソッドを呼び出す
  • ActiveHashなど、定数的なデータやDBアクセス不要なデータに向いてる
  • コントローラーの記述が不要で、シンプルに書ける

2.3 それぞれのメリット・デメリット

方法 メリット デメリット・注意点
インスタンス変数 - 複雑なデータ加工や条件分岐に柔軟
- ビューがすっきり
- テストしやすい
- コントローラーで毎回セットが必要
- 重複しやすい
直接モデル参照 - コントローラーがシンプル
- DRYな設計
- パフォーマンス良好
- ビューがやや長くなる
- 複雑な加工には不向き

2.4 コードの記述方法とポイント

  • インスタンス変数は、コントローラーでセット→ビューで参照
  • 直接モデル参照は、ビューでModel.where(...)などをそのまま記述
  • ActiveHashや定数的なデータは直接参照が推奨されることが多い
  • DBから取得したり、複雑なロジックが必要な場合はインスタンス変数が便利

2.5 DBモデルの場合の注意

  • User.allやProduct.allなどDBアクセスが発生するモデルは、毎回ビューで直接呼び出すとリクエストごとにSQLが発行されパフォーマンスが低下してしまう
  • DBモデルはインスタンス変数でコントローラーから渡すのが基本

3. バリデーションエラー時の値・エラー保持とリダイレクト

3.1 render :new の場合

  • バリデーションエラー時にrender :newで同じ画面に戻す
  • 入力値・エラーは@itemに保持されるので、フォームの値もエラーもそのまま表示できる

3.2 redirect_to new_path で値・エラーを保持する方法

  • 通常のredirect_toでは入力値・エラーが消えてしまう
  • セッション(session)を使って一時的に値・エラーを保存し、newアクションで復元することで「リダイレクト+値・エラー保持」を実現

3.3 コード例と解説

def create
  @item = Item.new(item_params)
  if @item.save
    redirect_to root_path
  else
    session[:item_params] = item_params.to_h
    session[:item_errors] = @item.errors.messages
    redirect_to new_item_path
  end
end

def new
  if session[:item_params]
    @item = Item.new(session[:item_params])
    if session[:item_errors]
      session[:item_errors].each do |attr, messages|
        messages.each { |msg| @item.errors.add(attr, msg) }
      end
    end
    session.delete(:item_params)
    session.delete(:item_errors)
  else
    @item = Item.new
  end
end
  • createでエラー時に値・エラーをセッションに保存し、リダイレクト
  • newでセッションから値・エラーを復元し、フォームに反映

3.4 renderとredirect_toの違い

  • renderはリクエストを再発行せず、URLは変わらない。入力値・エラーはそのまま保持される
  • redirect_toはリクエストを再発行し、URLが変わる。通常は入力値・エラーが消えるが、セッション等で保持可能

3.5 セッション利用の注意点

  • セッションはサイズ制限があり、大量データや機密情報の保存には向かない
  • フォームの一時的な値・エラー保持用途に限定して使うのが安全

4. エラーメッセージの日本語化

4.1 ja.ymlの設定

ja.yml 例

ja:
  activerecord:
    attributes:
      item:
        name: "商品名"
        # ...他の属性
  errors:
    messages:
      blank: "を入力してください"
      other_than: "を選択してください"
      # ...他のメッセージ

application.rb

config.i18n.default_locale = :ja

4.2 テストコードの修正

  • テストの期待値も日本語エラー文に合わせて修正が必要
  • 例:
    "カテゴリー を選択してください"
    "ニックネーム を入力してください"

4.3 I18n設定の確認

  • 日本語化が反映されない場合は、application.rbのconfig.i18n.default_locale = :jaを再確認すること

5. テストコードの記述例と注意点

5.1 直接モデル参照の場合

it 'カテゴリーが---(id:1)では登録できない' do
  @item.category_id = 1
  @item.valid?
  expect(@item.errors.full_messages).to include('カテゴリー を選択してください')
end
  • ビューで直接モデル参照している場合、テストではインスタンス変数を意識しなくてよい

5.2 インスタンス変数を使う場合

describe 'GET #new' do
  it '@categoriesがセットされていること' do
    get :new
    expect(assigns(:categories)).to eq(Category.where.not(id: 1))
  end
end
  • コントローラーやビューのテストで「インスタンス変数がセットされているか」を検証できる

6. まとめ

  • ActiveHash等は直接モデル参照でOK。複雑なデータや再利用性重視ならインスタンス変数も有効
  • DBモデルはインスタンス変数で渡すのが基本
  • リダイレクト時の値・エラー保持にはセッションを活用(用途・サイズに注意)
  • エラーメッセージ日本語化時はテストも日本語に修正。I18n設定も要確認
  • 実装とテストの前提が揃っていれば、どちらの方法でも問題なく動作する

まとめのまとめ(本日の作業の所感)

保守性・要件に合わせて、「インスタンス変数」と「直接モデル参照」を使い分けていきたい。
また、バリデーションエラー時のUXやテストの書き方も、実装方針に合わせて柔軟に対応することが大切。

1
0
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
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?