スクラッチでRails3に挑戦 Part 5(新規登録と削除処理)

これでとりあえず締めになります.

CRUD操作の「C(Create)」と「D(Delete)」がまだ実装されていません.まずは,「C」のレコード新規登録処理を追加したいと思います.まずは,routes.rbにStudentsコントローラのnewアクションを実行できるようにルーティング情報を設定します.

「students/new」は「students/:id」でマッチされないように,「students/:id」の行より前のほうに持って行っておきます.そして,index画面から新規登録のページへのリンクのために,「new_student」というパス名を設定しておきます.

[config/routes.rb]

Sample::Application.routes.draw do  match 'students', :to  => 'students#index'
  match 'students/new', :to => 'students#new', :as => 'new_student'
  match 'students/:id', :to => 'students#show', :as => 'student', :via => :get
  match 'students/:id', :to => 'students#update', :via => :put
  match 'students/:id/edit', :to  => 'students#edit', :as => 'edit_student'
   :
end

それでは,Studentsコントローラにnewメソッドを追加します.

[app/controllers/students_controller.rb]

# -*- coding: utf-8 -*-
class StudentsController < ApplicationController
  :
  def new
    @student = Student.new
  end
end

ActiveRecordのnewメソッドで新規レコードオブジェクト作成し,インスタンス変数 @student に格納します.
new アクションを指定したときに,リダイレクトされるビューテンプレート「new.html.erb」を作成します.基本的に「edit.html.erb」と似ているので,コピーしてモディファイしても良いでしょう.

[app/views/students/new.html.erb]

<h1>学生情報の新規登録</h1>

<%= form_for @student do |f| %>
<ul>
    <li>ID: <%= f.text_field :id %></li>
    <li>氏名: <%= f.text_field :name %></li>
    <li>E-mail: <%= f.text_field :email %></li>
</ul>
<%= f.submit '登録' %>
<% end %>

<%= link_to "一覧", students_path %>

オブジェクト「@student」の中身は空なので,ボックスの中身も空の状態です.

最後に,学生情報一覧画面(index)からヘルパーメソッド「link_to」を使ってリンクを張ります.

[app/views/students/index.html.erb]

<h1>学生情報一覧</h1>

<%= link_to "新規登録", new_student_path %>

<table>
 :

次に,この「登録」ボタンをクリックしたときに,ユーザが入力したデータをテーブルにinsertする処理,すなわち「create」アクションを定義します.

その前に,このFORMタグのactionオプションとmethodオプションを確認します.先ほどの新規登録のフォームをソースで確認すると...

<form accept-charset="UTF-8" action="/students" class="new_student" id="new_student" method="post">

ということで,

  • action は,”/students”
  • method は,”post”

となっています.

一方,編集画面「edit.html.erb」のフォームも同様「<%= form_for @student do |f| %>」で定義していましたが,ソースで確認すると...

<form accept-charset="UTF-8" action="/students/123456890" class="edit_student"
id="edit_student_1234567890" method="post">
 :
<input name="_method" type="hidden" value="put" />

ということで,

  • action は,”/students/:id”
  • method は,”post”
  • _method は,”put”

となっています.

これはどこで判断しているのでしょうか,おそらくform_forで指定している「@student」オブジェクト他ないと思います.つまり...

new.html.erb の form_for の @student
Student.new でインスタンス生成直後 → オブジェクトの中身が空 → データの登録 → POSTメソッド
edit.html.erb の form_for の @student
Student.find(ID)でインスタンス取得 → オブジェクトの中身が存在 → データの更新 → PUTメソッド

というように解釈しているのではないのでしょうか...
今回の新規登録の際は,POSTメソッドで,URL「/students」でアクセスしたときに「create」アクションできるようルーティング情報を設定します.

match 'students', :to => 'students#create', :via => :post

ここで注意が必要なのが.同じURLパスをもつルーティング情報

match 'students', :to => 'students#index'

の存在です.こちらを先に記述する(優先する)と,createアクションが使われない可能性が出てきます.indexは情報を取得するだけですので,これに対し,GETメソッドの指定をしておきます.よって,最終的なroutes.rbは以下のとおり.

[config/routes.rb]

Sample::Application.routes.draw do
  match 'students', :to => 'students#index', :via => :get
  match 'students', :to => 'students#create', :via => :post
  match 'students/new', :to => 'students#new', :as => 'new_student'
  match 'students/:id', :to => 'students#show', :as => 'student', :via => :get
  match 'students/:id', :to => 'students#update', :via => :put
  match 'students/:id/edit', :to  => 'students#edit', :as => 'edit_student'

ついでに「rake routes」の結果も掲載しておきます.

$ rake routes
    students GET  /students(.:format)          {:action=>"index", :controller=>"students"}
             POST /students(.:format)          {:action=>"create", :controller=>"students"}
 new_student      /students/new(.:format)      {:controller=>"students", :action=>"new"}
     student GET  /students/:id(.:format)      {:controller=>"students", :action=>"show"}
             PUT  /students/:id(.:format)      {:controller=>"students", :action=>"update"}
edit_student      /students/:id/edit(.:format) {:controller=>"students", :action=>"edit"}

それでは,Studentsコントローラにcreateメソッドを定義します.

[app/controllers/students_controller.rb]

:
def create
  @student = Student.new(params[:student])
  @student.save
  redirect_to students_path
end
:

新規登録フォームより受け渡されたデータ(paramsパラメータ)を使ってインスタンスを作成し,saveメソッドでcommitします.これで終わるとRailsはcreate.html.erbにリダイレクトしようとします.そんなビューテンプレートは作成していないので,index画面に戻るようredirect_toでindex画面に移るよう指定します.

とりあえずこれで何か登録してみます.

ところがこれではエラー「idが0でなんたら...」が出て上手くいきません.そこでログを見てみます.

Started POST "/students" for 127.0.0.1 at 2011-11-10 17:27:01 +0900  Processing by StudentsController#create as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"RuWgfdKyuie3E4lJmFrOL4peA9IG6rYgr+fNtBNMbuA=",
  "student"=>{"id"=>"1234567892", "name"=>"西郷隆盛", "email"=>"taka@kago.jp"}, "commit"=>
  "登録"}
WARNING: Can't mass-assign protected attributes: id
   (0.1ms)  BEGIN
  SQL (0.5ms)  INSERT INTO `students` (`email`, `name`) VALUES ('taka@kago.jp', '西郷隆盛')
   (0.1ms)  COMMIT
Redirected to http://localhost:3000/students/0
Completed 302 Found in 11ms

Started GET "/students/0" for 127.0.0.1 at 2011-11-10 17:27:02 +0900
  Processing by StudentsController#show as HTML
  Parameters: {"id"=>"0"}
  Student Load (0.2ms)  SELECT `students`.* FROM `students` WHERE `students`.`id` = '0' LIMIT 1
Completed 404 Not Found in 46ms

無事にINSERTクエリーがCOMMITされていますが,最終的には「404 Not Found」エラーが出ています.

そうです.フィールド「id」の値が入力されていません.ユーザが入力したidの値がINSERTクエリー時に除外されています.これは,ActiveRecordでは,テーブルのidフィールドは「auto increment」即ち,自動連番で定義されていることが前提条件だからです.ちなみに今回のstudentsテーブルのフィールドidは,auto incrementは設定していません.

それでは実際のテーブルの中身をデータベースコンソールで確認してみます.

$ rails db
Enter password: foobar

mysql> select * from students;
+------------+--------------------+--------------+
| id         | name               | email        |
+------------+--------------------+--------------+
| 1234567890 | Taro カゴシマ      | taro@kago.jp |
| 1234567891 | 薩摩花子           | hana@kago.jp |
|            | 西郷隆盛           | taka@kago.jp |
+------------+--------------------+--------------+

idが空のレコードができてしまっています...これをとりあえずSQLコマンド直打ちで削除しておきます.

mysql> delete from students where id='';

mysql> \q

カスタマイズしたidフィールドにデータを入れるにはどうしたら良いか...それは,次のようにして一つ一つフィールドのアクセサに代入して行くしかないようです.

[app/controllers/students_controller.rb]

 :
def create
  @student = Student.new do |s|
    s.id = params[:student][:id]
    s.name = params[:student][:name]
    s.email = params[:student][:email]
  end
  @student.save
  redirect_to students_path
end
 :

最後に「削除」です.

削除も,登録,更新のように特に確認のページ(ビューテンプレート)は必要なさそうです.どのidのレコードを削除するのかという情報が必要なので,ルートパスは「students/:id」の形になります.後は,アクションとして「destroy」を,HTTPメソッドとして「DELETE」を指定します.

よって,routes.rbは以下のとおりとなります.

[config/routes.rb]

Sample::Application.routes.draw do
  match 'students', :to => 'students#index', :via => :get
  match 'students', :to => 'students#create', :via => :post
  match 'students/new', :to => 'students#new', :as => 'new_student'
  match 'students/:id', :to => 'students#show', :as => 'student', :via => :get
  match 'students/:id', :to => 'students#update', :via => :put
  match 'students/:id', :to => 'students#destroy', :via => :delete
  match 'students/:id/edit', :to  => 'students#edit', :as => 'edit_student'
   :
end

編集時と同様,findメソッドでレコードを特定します.その後,destroyメソッドでレコードを削除します.削除後はstudents_pathへリダイレクト,即ちindex画面に移ります.

[app/controllers/students_controller.rb]

def destroy
  @student = Student.find(params[:id])
  @student.destroy
  redirect_to students_path
end

学生情報一覧(index)画面を修正します.

[app/views/students/index.html.erb]

 :
<% @students.each do |student| %>
  <tr>
    <td><%= student.id %></td>
    <td><%= student.name %></td>
    <td><%= student.email %></td>
    <td><%= link_to "表示", student_path(student) %></td>
    <td><%= link_to "編集", edit_student_path(student) %></td>
    <td><%= link_to "削除", student_path(student), :confirm => '本当に消してもいいの?', :method => :delete %></td>
  </tr>
<% end %>
 :

ポイントとしては,link_toメソッドで,「student_path(student)」に飛ぶように設定していますが,このままだと「表示」のリンクと同じになってしまいますので,HTTPメソッドをDELETEで発行するために,「:method => :delete」を付加しています.
また,同じくlink_toメソッドに「confirm」オプションを付加しています.こうすると,削除のリンクをクリックしたときに,次のような確認ダイアログが出ます.

これで完成です.

広告

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中