Edited at

Rails renderする際は変数名に気をつける


はじめに

先日、各ページに共通のヘッダーを持たせる処理を行ったところ、詳細ページで500エラーが...。

考えればすぐに分かることだったのですが解決に時間を要したためメモ。


共通ヘッダーを作る

今回はArticlesの公開日の新しい3件のタイトルと公開日をヘッダーに表示させます。

用意したmigrationファイルはこちら。


db/migrate/201908081212_create_articles.rb

class CreateArticles < ActiveRecord::Migration

def change
create_table :articles do |t|
t.string :title
t.text :body
t.datetime :published_at
t.timestamps
end
end
end

modelには公開日の新しい3件を取得する処理を書く。


app/models/article.rb

class Article < ApplicationRecord

def self.header
Article.where('published_at <= ?', Time.zone.now).limit(3).order(published_at: :desc)
end
end

ヘッダー用のviewファイルはこんな感じ。

@articleに3件のデータを入れて、each doで回していく。


app/views/common/_header.html.erb

<div class ="header">

#もろもろ省略してArticles表示
<ul class="hoge">
<% @article = Article.header %>
<% @article.each do |article| %>

<li class="hoge-list"><a href="/article/<%= article.id%>"><span class="hoge-date"><%= article.published_at.strftime('%Y.%m.%d')%></span><%= article.title %></a></li>
<% end %>
</u
l>
</div>

各ページのviewファイルに、<%= render partial: "common/header" %>を追記して、ヘッダーを表示、ヘッダーの中には新しい3件のArtitlesを表示できました。


しかし、各Articleのページを表示しようとすると500errorが・・・!

エラー内容は、undefined method `title' for #<Article::ActiveRecord_Relation:xxxxxxxx>でした。


viewとコントローラを確認してみる

まずはviewを確認。


app/views/article/show.html.erb

#先程追記したヘッダー

<%= render partial: "common/header" %>
<div class=
"sample">
<h1><%= @article.title %></h1>
</div>

・・・なるほど、@articleにtitleなんか無いよと。

@articleを設定したコントローラ、showアクションを見てみます。


app/controllers/article_controller.rb

class NewsInfoController < ApplicationController

#indexアクションとかは省略
def show
@article = Article.find(params[:id])
end
end

んー、@articleに中身が入ってない?と考え、pp @articleでターミナルに表示させてみると、データは取れている。

@article.titleも、データが取れる。何故・・・?


もう一度エラーを見直してみる

そうだ、エラー内容はundefined method `title' for #<Article::ActiveRecord_Relation:xxxxxxxx>

showアクションでは、@articleはfindメソッドで取っているのでActiveRecord_Relationでは無い。

一連でActiveRecord_Relationを使った--where使ったのは・・・

そうか、ヘッダーの処理だ!!


ヘッダーのviewファイルを見直す


app/views/common/_header.html.erb

#もろもろ省略

<% @article = Article.header %>
<% @article.each do |article| %>

<li class="hoge-list"><a href="/article/<%= article.id%>"><span class="hoge-date"><%= article.published_at.strftime('%Y.%m.%d')%></span><%= article.title %></a></li>
<% end %>

ここにも@articleと名前を付けていました。この@articleは、whereを使っているのでActiveRecord_Relationですね。

show.html.erbでrenderしてきた、_header.html.erb内の@articleが先に読み込まれたためエラーが出たのです。

なので、_header.html.erb内の@article@articles@article_headerに名前を変えたところ、解決しました。


app/views/common/_header.html.erb

#もろもろ省略

<% @articles = Article.header %>
<% @articles.each do |article| %>

<li class="hoge-list"><a href="/article/<%= article.id%>"><span class="hoge-date"><%= article.published_at.strftime('%Y.%m.%d')%></span><%= article.title %></a></li>
<% end %>

初めから複数形にしておけばよかったのですが…実際は@newsだったので分かりづらかった・・・。


まとめ

・部分テンプレート内(呼び出される側)で変数を用いる場合は、呼び出すviewファイル内と同じ変数名を付けないよう注意する。

・whereには複数、findには単数を付けるなど、取得するレコードの数に応じて変数名を工夫する。

初心者ですので、間違っている点等あればご指摘いただけますと幸いです。