さて、次は投稿の編集。

Controller はとってもシンプル。
app/controllers/posts_controller.rb
# GET /posts/1/edit def edit @post = Post.find(params[:id]) end
View は、new とほぼ同じ。
app/views/posts/edit.html.erb
<h1>Editing post</h1>
<% form_for(@post) do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :name %><br />
<%= f.text_field :name %>
</p>
<p>
<%= f.label :title %><br />
<%= f.text_field :title %>
</p>
<p>
<%= f.label :content %><br />
<%= f.text_area :content %>
</p>
<p>
<%= f.submit "Update" %>
</p>
<% end %>
<%= link_to 'Show', @post %> |
<%= link_to 'Back', posts_path %>
違うのは、link_to 'Show', @post の部分くらいか。
第二引数に @post が入っている。これはなかなか直感的な記述だ。
ここで重要な点。formタグの部分をソース表示してみよう。
<form action="/posts/1" class="edit_post" id="edit_post_1" method="post">
new のときは、こうだった。
<form action="/posts" class="new_post" id="new_post" method="post">
action のURLが変わっている。
もう一度、erbファイルを見てみる。
form_for(@post)
とある。
new のとき、ここに入る @post は以下。
@post = Post.new
一方、edit のときは以下。
@post = Post.find(params[:id])
前者の @post には id は入っていない。後者には入っている。
この違いが、form_for が生成するformタグのaction属性の違いになっている。
これは続く処理にも影響していく。

このように修正してUpdateしてみる。

早速 Controller を見る。
app/views/posts/edit.html.erb
# PUT /posts/1
# PUT /posts/1.xml
def update
@post = Post.find(params[:id])
respond_to do |format|
if @post.update_attributes(params[:post])
flash[:notice] = 'Post was successfully updated.'
format.html { redirect_to(@post) }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => @post.errors, :status => :unprocessable_entity }
end
end
end
PUT /posts のときには create に処理が移ったが
PUT /posts/1 のときには update に処理が移る。これもRailsのルール。
あと、new のときと異なるのは @post.update_attributes(params[:post]) の部分。
ここでUPDATE文を発行している。
データベースの中身も見てみよう。

content, updated_at が更新されている。
最後に、投稿を削除する。
メインページをもう一度見てみよう。
app/views/posts/index.html.erb
<td><%= link_to 'Destroy', post, :confirm => 'Are you sure?', :method => :delete %></td>
今までの link_to と比べて、:confirm, :method が新たに追加されている。
では画面から Destroy をクリックしてみよう。

このように確認ダイアログが表示された。ここでOKを押せば、DELETE形式でSUBMITが実行される。
app/controllers/posts_controller.rb
# DELETE /posts/1
# DELETE /posts/1.xml
def destroy
@post = Post.find(params[:id])
@post.destroy
respond_to do |format|
format.html { redirect_to(posts_url) }
format.xml { head :ok }
end
end
DBからレコードを削除し、トップ画面に遷移している。

さて、これをどうやって実現しているか。見てみよう。
<a href="/posts/1" onclick="
if (confirm('Are you sure?')) {
var f = document.createElement('form');
f.style.display = 'none';
this.parentNode.appendChild(f);
f.method = 'POST';
f.action = this.href;
var m = document.createElement('input');
m.setAttribute('type', 'hidden');
m.setAttribute('name', '_method');
m.setAttribute('value', 'delete');
f.appendChild(m);
var s = document.createElement('input');
s.setAttribute('type', 'hidden');
s.setAttribute('name', 'authenticity_token');
s.setAttribute('value', '55b4623197413de82f6eb9731811d8d30d7c5b4f');
f.appendChild(s);
f.submit();
};
return false;
">
Destroy
</a>
なかなかタフなことをやっている。
簡単に言えば、動的にformエレメントを作成してsubmitを叩いている。
作成されたformエレメントは、以下のようになるだろう。
<form method="post" action="/posts/1" style="display:none"> <input type="hidden" name="_method" value="delete" /> <input type="hidden" name="authenticity_token" value="55b4623197413de82f6eb9731811d8d30d7c5b4f" /> </form>
f.action = this.href; というのがニクい(?)。
_method で、HTTP Method Type を指定しているらしい。おそらくこれは Rails の独自仕様。
authenticity_token は、セッションIDのようなもの。
おそらく、二人以上が同時に一つの投稿を編集しようとした場合などに使われるはず。
以上で、index / new / create / update / destroy と一通りの処理を追ってみた。
ここまでのまとめ。
| URL | Http Method | アクション名 |
|---|---|---|
| /posts | GET | index |
| /posts/new | GET | new |
| /posts | POST | create |
| /posts/NO | GET | show |
| /posts/NO/edit | GET | edit |
| /posts/NO | POST | update |
| /posts/NO | DELETE | destroy |