STEP 5: レコードの新規追加

2010/02/01

今回は、タスクの追加機能を実装しましょう。

大まかな作業手順は次の通りです。

  • 入力フォームのテンプレートを作ります。
  • Tasks コントローラに new アクションを追加します。
  • タスク一覧ページに「新規タスクの追加」リンクを設置します。
  • Tasks コントローラに create アクションを追加します。
  • Task モデルに、値の検証(validation)を行うコードを追加します。
  • 入力フォームのテンプレートにエラーメッセージを表示するコードを追加します。
  • Task モデルに、値の初期化コードを追加します。

入力フォームのテンプレートを作ります。

$ edit app/views/tasks/new.html.erb
<h1>新規タスクの追加</h1>

<% form_for @task do |f| %>
<table class="attributes">
  <tr>
    <th>件名</th>
    <td><%= f.text_field :subject %></td>
  </tr>
  <tr>
    <th>期日</th>
    <td><%= f.date_select :due_date %></td>
  </tr>
  <tr>
    <th>完了</th>
    <td>
      <%= f.radio_button :done, true %> Yes
      <%= f.radio_button :done, false %> No
    </td>
  </tr>
  <tr>
    <th>注記</th>
    <td><%= f.text_area :note, :size => '40x5' %></td>
  </tr>
</table>

<div class="submit">
  <%= f.submit '追加' %>
</div>
<% end %>

Tasks コントローラに new アクションを追加します。

$ edit app/views/tasks/new.html.erb
class TasksController < ApplicationController
  (省略)

  def show
    @task = Task.find(params[:id])
  end

  def new
    @task = Task.new
  end

  (省略)
end

アクションを定義する順番はアプリケーションの動作に影響を与えませんが、普通は index, show, new, edit, create, update, destroy の順で記述します。


タスク一覧ページに「新規タスクの追加」リンクを設置します。

$ edit app/views/tasks/index.html.erb
<h1>タスク一覧</h1>

<div class="commands">
  <%= link_to '新規タスクの追加', [ :new, :task ] %>
</div>

<table class="list">
(省略)

ここでは link_to の第2引数に [ :new, :task ] というシンボルの配列を指定しています。このように書くと、/tasks/new というURLパスへのリンクが生成されます。

詳しいことはいずれ説明しますので、今のところはこのまま納得してください。

なお、:task が「単数形」である点に注意してください。


では、ここで表示を確認してみましょう。

データベースの状態をリセットするため、テストフィクスチャをロードし直します。

$ rake db:fixtures:load

ブラウザでタスク一覧のページを表示し直します。

画面キャプチャ1

新たに設置された「新規タスクの追加」リンクをクリックすると、入力フォームが表示されます。

画面キャプチャ2

日付を選択するセレクトボックスが英語表記になっている点については気にしないでください。アプリケーションの日本語化は、後回しにします。


続いて、Tasks コントローラに create アクションを追加します。

$ edit app/controllers/tasks_controller.rb
class TasksController < ApplicationController
  (省略)

  def new
    @task = Task.new
  end

  def create
    @task = Task.new(params[:task])
    if @task.save
      redirect_to :tasks
    else
      render :action => 'new'
    end
  end

  (省略)
end

入力フォームに適当な値を入力して、「追加」ボタンをクリックします。

画面キャプチャ3

タスク一覧に新しいタスクが追加されています。

画面キャプチャ4


次に、「件名」フィールドの値が空であった場合に、エラーメッセージとともに入力フォームが再表示されるようにします。

データベーステーブルに格納されるレコードの値が正しいかどうか確かめることを値の検証と呼びます。

Task モデルに、値の検証を行うコードを追加します。

$ edit app/models/task.rb
class Task < ActiveRecord::Base
  validates_presence_of :subject
end

値の検証はコントローラのアクションの中で行うのではなく、モデルの中で行います。

これは、Ruby on Railsプログラミングの最も重要な原則の1つです。


入力フォームのテンプレートにエラーメッセージを表示するコードを追加します。

$ edit app/views/tasks/new.html.erb
<h1>新規タスクの追加</h1>

<% form_for @task do |f| %>

<%= f.error_messages %>

<table class="attributes">
(省略)

CSSファイルに設定を追加します。

$ edit public/stylesheets/application.css
(省略)

div.errorExplanation {
  background-color: #fcc;
  border: 1px solid #f00;
  padding: 3px;
}

div.errorExplanation h2 {
  color: #f00;
  padding: 3px;
  margin: 0;
  border: none;
}

div.errorExplanation p {
  padding: 3px;
}

.fieldWithErrors {
  background-color: #fcc;
  color: #000;
  padding: 2px;
}

入力フォームの「件名」フィールドを空にしたまま、「追加」ボタンをクリックすると、次のような画面になります。

画面キャプチャ5

エラーメッセージが英語表記になっていますが、「件名(subject)は空ではダメ」と正しい判定を行っています。

続いて「件名」に何か文字を入力して「追加」ボタンをクリックすると、タスクが追加されます。値の検証は正しく動いているようですね。


これでだいたいOKですが、1つ気になる点があります。

入力フォームの初期状態で、完了フィールドの値を選ぶ2つのラジオボタンのいずれもチェックされていない、ということです。

常識的には「No」が選ばれている状態で始まるべきです。

そうするためには、Task モデルに次のような初期化コードを追加します。

class Task < ActiveRecord::Base
  validates_presence_of :subject

  protected
  def after_initialize
    self.done = false if self.done.nil?
  end
end

after_initialize メソッドは、モデルクラスが「インスタンス化」された直後に実行されるメソッドです。このように、何かのきっかけで実行されるメソッドをコールバックと呼びます。

コールバックメソッドの可視性は public ではなく protected にします。

ところで、Tasksコントローラのnewアクションの中で @task = Task.new(:done => false) のように書いても同じ効果が得られます。しかし、モデルオブジェクトに初期値を与える仕事はコントローラの領域とは言い難いので、モデルに記述した方が好ましいと思われます。


では、タスク一覧ページに戻って「新規タスクの追加」リンクをクリックしてみましょう。

画面キャプチャ6

初めから、完了フィールドの「No」が選択された状態になっている点を確認してください。