ソフトウェアの一覧表示でキーワードを表示
一覧表示でKeyword_id列を表示したい。
キーワードテーブルを関連付ける下準備は全て終わった...。ここでソフトウェアの一覧表示にkeyword_id列が表示されているのを期待して、アクセスしてみる。created_on、updated_onの時は、フィールドを追加しただけで表示されたのだから、今回も当然、表示されると考えたのだ。
しかし、webブラウザではkeyword_idの列はどこにも見当たらない...。念のため、CocoaMySQLでテーブルの状態を確認してみると、keyword_idフィールドはちゃんと登録されている。
よくよく考えてみたら、idフィールドだって表示されてない...。list.rhtnlで描画部分のコードを確認してみると...
- ビュー list.rhtml
...(途中省略)... <% for column in Software.content_columns %> <th><%= column.human_name unless column.name == 'url'%></th> <% end %> ...(途中省略)... <% for column in Software.content_columns %> <td><%= link_to_if column.name == 'title', h(software.send(column.name)), URI.encode(software.send('url')) unless column.name == 'url' %></td> <% end %> ...(途中省略)...
列タイトルも、その項目の値も、どちらもSoftware.content_columnsで、Softwareモデルのcontent_columnsメソッドを呼び出して、フィールドを取り出している。きっと、content_columnsメソッドではidや_idで終わるフィールドは無視される仕様なのかもしれない...。
そこで、自分でタイトルと項目の値の描画コードを1行ずつ追加した。
- ビュー list.rhtml
<h1>Listing softwares</h1> <table> <tr> <% for column in Software.content_columns %> <th><%= column.human_name unless column.name == 'url'%></th> <% end %> <th>Keyword</th> </tr> <% for software in @softwares %> <tr> <% for column in Software.content_columns %> <td><%= link_to_if column.name == 'title', h(software.send(column.name)), URI.encode(software.send('url')) unless column.name == 'url' %></td> <% end %> <td><%=h software.keyword_id %></td> <td><%= link_to 'Show', :action => 'show', :id => software %></td> <td><%= link_to 'Edit', :action => 'edit', :id => software %></td> <td><%= link_to 'Destroy', { :action => 'destroy', :id => software }, :confirm => "#{software.title} is deleted. Are you sure?", :post => true %></td>
すると、こんな感じでKeyword列が表示された。フィールドを追加したばかりなので、キーワード列の中身は何も無いが...。
キーワードを選択して入力したい。
今度は、新規作成や編集ページでキーワードをプルダウンリストから選択して入力するようにしたい。
入力フォームの描画は、_form.rhtnlで処理しているので、以下のようにコードを追加した。
- ビュー _form.rhtml
...(途中省略)... <p><label for="software_keyword_id">Keyword</label><br/> <%= collection_select :software, :keyword_id, Keyword.find(:all), :id, :name %></p>
重要なのは、collection_selectの構文。第1引数、第2引数は、text_fieldやtext_areaと同じ意味合い。自分としては、以下のように理解した。
要素 実例 意味合い 第1引数 :software コントローラから渡されるインスタンス変数@softwareを対象にする。 第2引数 :keyword_id @softwareが示すkeyword_idフィールドが対象になる。 第3引数 Keyword.find(:all) キーワードテーブルからすべてを抽出したレコードの固まりを設定する。 第4引数 :id keyword_idと連動するフィールド。idフィールドの値が、keyword_idフィールドに設定される。 第5引数 :name プルダウンリストに表示するフィールド。nameフィールドの値が表示される。
これで、キーワードを選択して表示できるようになった。こんな感じ。
しかし、一覧表示で見るとkeyword_idの値が数値として見えている。
一覧表示でちゃんとキーワードを表示したい。
本当に見たいのは、その値が示すキーワードテーブルのnameフィールドの内容だ。そこで以下のようにlist.rhtmlを修正した。
<td><%=h software.keyword.name %></td>
software.keyword_idとなっている箇所を、software.keyword.nameに修正した。_idが、.nameに置き換わっただけ。
これで、keyword_idの値をキーに、キーワードテーブルのnameフィールドの値を取り出せるはず...。が...、しかし、 またもやエラー発生。
このエラーには結構はまった...。
- 何か1文字、入力を間違ったかと思い(また複数形のsとか...)、注意深く確認したが問題なし。
- サーバーがおかしいのかと思い、何度かServersタブで再起動。しかしエラーは直らず。
- しまいには、パソコンがおかしいのかと思いMacBookまで再起動。でもエラーは直らず。
本日は諦めようと思ったその時に、何かが閃いた。そう、おかしいのはサーバーでもパソコンでもない、自分の設定したデータベースの値だった...。
You have a nil object when you didn't expect it! The error occured while evaluating nil.name
よく見ればエラーメッセージの中にも書いてあるじゃないか...。nil object、nil.nameにもっと早く気付けばよかった...。
つまり、途中からkeyword_idフィールドを追加したので、項目の内容が何もない状態のデータ(nilと呼ばれる値)がkeyword_idフィールドに設定されていて、その状態でsoftware.keyword.nameメソッドを呼び出すと、何もないものに対する呼び出しのためエラーになっていたのだ。
編集ページから、全部のソフトウェアにキーワードを設定してあげれば済むのだが、今後のことも考えて、以下のようにコードも修正した。
- ビュー list.rhtml
<td><%=h software.keyword.name unless software.keyword_id == nil %></td>keyword_idがnilでない時だけ、software.keyword.nameを処理するようにした。
- モデル software.rb
validates_presence_of :title, :description, :url, :keyword_id validates_numericality_of :keyword_idkeyword_idは、入力されていること、数値であることを検証するようにした。