woshidan's loose leaf

ぼんやり勉強しています

Rails6.1になってコントローラのテンプレート指定に拡張子をつけることが推奨されなくなった

Rails 6.1以上になってCSVなどをダウンロードさせるコントローラのアクションに

def download
    render(
      csv: 'users_list',
      template: 'users/download.csv.ruby'
    )
  end

のように書いているとRSpecの実行時に

DEPRECATION WARNING: Rendering actions with '.' in the name is deprecated: users/download.csv.ruby (called from download at ./app/controllers/users_controller.rb:14)

のように注意されるようになりました。これに対応するには、

def download
    render(
      csv: 'users_list',
      template: 'users/download'
    )
  end

と直せばよいのですが、それだけだと今度は views/users/download.csv.ruby が存在していてもダウンロード時に対応するフォーマットのテンプレートが存在しないというエラーになってしまいます。

なぜかというと、デフォルトではダウンロード時のリンクで

<% link_to 'ダウンロード', download_users_path %>

としたとき、ダウンロードのフォーマットはtext/htmlになっています。コントローラに拡張子を書いていたときはその拡張子に沿ってテンプレートを探してくれていましたが、コントローラから拡張子の指定をとるとデフォルトの設定が優先されます。なので、 users/download.csv.rubycsvのファイルが存在していても探しにいくのはtext/html形式のファイルなので見つからない、ということになります。

text/csv 形式のファイルを探しに行ってもらうためにはダウンロードリンクを用意するときに、

<% link_to 'ダウンロード', download_users_path(format: :csv) %>

と、URLヘルパーにformat属性の指定を行えばよいです。こうすると、コントローラに拡張子を書かなくともテンプレートで用意した拡張子のファイルを探しに行ってくれるようになります。このフォーマットはconifg/routes.rbで

resources :users do
  get :download, on: :collection, default: { format: :csv }
end

のようにconfig/routes.rbの方でも指定できます。

参考

railsguides.jp