Railsでモデル名とコントローラ名が異なる時にparamsで渡すパラメータ値

コントローラー名とモデル名が異なる時のルートパスでつまずいたので、忘れないようにメモ。

前提条件

コントローラー名①: UsersController
コントローラー名②: DatasController
モデル①: HouseModel (has_many :datasets)
モデル②: DatasetModel (belongs_to :house)
各houseデータは複数のdatasetでーたを持っているため、Datasetモデルはhouse_idで繋がっている。

まずやってみたこと

今回は複数のuserが各々dataを持っており、各userページでdataを表示させるのが目標。
そのためコントローラーをネストさせる必要がある。
config/routes.rb

Rails.application.routes.draw do
  root "users#index"
  resources :users, only: [:index] do
    resources :datas, only: [:index]
  end
end

DBに作成したモデルはベースとなるcsvファイルがあったので、UserテーブルではなくHouseテーブルとなってる。
index.html.hamlでは一覧を表示したかったので、モデルから全データを取得しておく。
app/controllers/users_controller

class UsersController < ApplicationController
  def index
    @houses = House.all
  end
end

続いてdataの方に飛ばすためのcontrollerを設定
app/controllers/datas_controller

class DatasController < ApplicationController
  before_action :set_house, only: [:index]

  def index
  end

  private
  def set_house
    @house = House.find(params[:id])
  end
end

一覧を表示させるようにhamlファイルを作成。
今回は一覧表示が目標なので、表形式の表示をさせるためtableを使用する。
名前を押したらユーザーのデータを表示するようにしたいので、link_toは名前のところに設定。
app/views/users/index.html.haml

%table
  %thead
    %tr
      %th No.
      %th Family Name
  %tbody
    - @houses.each do |house|
      %tr
        %td
          =house.id
        %td
          = link_to user_datas_path(id: house.id) do
            =house.Lastname

さて、これでサーバーを立ち上げて確認しようとするとerror...

間違い箇所

エラーの中身を読み解いてみると間違えてた場所がわかった。

= link_to user_datas_path(id: house.id) do

ここでid:は無くて、user_idじゃね?って出てきた。
というわけでさっそく変更

= link_to user_datas_path(user_id: house.id) do

paramsで受け取る方も合わせて変更する必要があるので修正
app/controllers/datas_controller

class DatasController < ApplicationController
  before_action :set_house, only: [:index]

  def index
  end

  private
  def set_house
    @house = House.find(params[:user_id])
  end
end

とりあえず流れた!

コードの読みやすさを考える

うーん…Houseモデルを使ってるのにuser_idの値を入れるっていうのが非常にわかりづらい…
というわけで結局UsersControllerをHousesControllerに名前を変えることに。

config/routes.rb

Rails.application.routes.draw do
  root "houses#index"
  resources :houses, only: [:index] do
    resources :datas, only: [:index]
  end
end


app/views/houses/index.html.haml

%table
  %thead
    %tr
      %th No.
      %th Family Name
  %tbody
    - @houses.each do |house|
      %tr
        %td
          =house.id
        %td
          = link_to house_datas_path(house_id: house.id) do
            =house.Lastname

app/controllers/datas_controller

class DatasController < ApplicationController
  before_action :set_house, only: [:index]

  def index
  end

  private
  def set_house
    @house = House.find(params[:house_id])
  end
end

これで

  • モデル名とコントローラ名が一致してわかりやすい
  • paramsで渡す値が'house_id'となって、Datasetテーブルのカラム名とも一致してわかりやすい

といいことづくめに。

まとめ

モデル名とコントローラ名は同じものにしておいた方が、データのやり取りをするときわかりやすい。
実際のurlの見栄えを考えて別名にすることもあるので、そういう時は今回のような対応をすればOK!