minimize

事業拡大のため、新しい仲間を募集しています。
→詳しくはこちら

edit

さて、次は投稿の編集。

edit_post.png

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_for

ここで重要な点。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属性の違いになっている。
これは続く処理にも影響していく。

post edit

edit_post_1.png

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

edit_post_result.png

早速 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文を発行している。

データベースの中身も見てみよう。

db_result_2.png

content, updated_at が更新されている。

destroy

最後に、投稿を削除する。
メインページをもう一度見てみよう。

app/views/posts/index.html.erb

<td><%= link_to 'Destroy', post, :confirm => 'Are you sure?', :method => :delete %></td>

今までの link_to と比べて、:confirm, :method が新たに追加されている。
では画面から Destroy をクリックしてみよう。

post_confirm.png

このように確認ダイアログが表示された。ここで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からレコードを削除し、トップ画面に遷移している。

posts_index.png

さて、これをどうやって実現しているか。見てみよう。

<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