← 上一章:Controller 下一章:CRUD 分解動作 - 簡易票選系統實作(下) →

CRUD 分解動作 - 簡易票選系統實作(上)

CRUD 是 Create(新增), Read(讀取), Update(更新) 跟 Delete(刪除) 四個字的縮寫,對 Rails 來說,開發 CRUD 應用程式可以說是它的強項,甚至用 Scaffold 一行指令就搞定。第一次用 Scaffold 會感覺非常神奇,但對新手來說也不知道到底發生什麼事。所以接下來這個章節,我們將一步一步分解 CRUD 的各項動作,希望可以讓大家更了解 Rails 的一些慣例以及 Scaffold 到底做了什麼事。

實作:票選系統

我們將實作一個簡易的投票系統,藉此熟悉 Rails 的 MVC 運作方式以及表單處理。在開始前,先再次複習一下這張 MVC 的圖解:

image

當你填寫表單完成並按下送出後,表單會以 GET 或 POST 的方式傳送資料,Route 就會根據路徑以及動作(HTTP Verb)來決定這次的任務該由哪一個 Controller 的哪一個 Action 處理。

系統功能

  1. 可以新增、修改、刪除候選人資料(姓名、黨派、年齡、政見,以上資料都是必填欄位)。
  2. 可以投票給候選人並顯示該候選人得票數。

第 00 步 - 修改 Route

Route 是整個 Rails 應用程式的第一關,所以在思考要新增功能的時候,我通常第一步都是先想 Route 要怎麼規劃。因為要可以新增、修改、刪除候選人,所以我使用 resources 直接做出完整的 CRUD 路徑,範例如下:

  # 檔案:app/config/routes.rb
  Rails.application.routes.draw do
    resources :candidates
  end

這樣就可以做出以下的路徑:

$ bin/rails routes
    Prefix Verb   URI Pattern                    Controller#Action
candidates GET    /candidates(.:format)          candidates#index
           POST   /candidates(.:format)          candidates#create  new_candidate GET    /candidates/new(.:format)      candidates#new edit_candidate GET    /candidates/:id/edit(.:format) candidates#edit
 candidate GET    /candidates/:id(.:format)      candidates#show
           PATCH  /candidates/:id(.:format)      candidates#update
           PUT    /candidates/:id(.:format)      candidates#update
           DELETE /candidates/:id(.:format)      candidates#destroy

resources 方法可以幫忙做出 8 個路徑,分別對到新增、修改、刪除等 7 個 Action。Route 相關介紹請參閱第 11 章

第 01 步 - 新增 Controller

有了路徑,知道要往哪邊去了,接下來就是把要去的地方完成。先使用 Rails 內建的產生器把 Controller 做出來:

$ bin/rails generate controller candidates
  Running via Spring preloader in process 9314
    create  app/controllers/candidates_controller.rb
    invoke  erb
    create    app/views/candidates
    invoke  test_unit
    create    test/controllers/candidates_controller_test.rb
    invoke  helper
    create    app/helpers/candidates_helper.rb
    invoke    test_unit
    invoke  assets
    invoke    coffee
    create      app/assets/javascripts/candidates.coffee
    invoke    scss
    create      app/assets/stylesheets/candidates.scss

透過產生器指令,除了 Controller 本身以及 View 目錄外,還順便也幫忙產生相關的測試檔、JavaScript(CoffeeScript)、CSS(SCSS)檔案。因為檔案其實不多也不複雜,所以即使手工自己建立檔案也沒什麼太大的問題。

第 02 步 - 新增 Model

讓我們想一下這個候選人(Candidate)的 Model 要有哪些欄位:

欄位名稱 資料型態 說明
name 字串(string) 候選人姓名
party 字串(string) 政黨
age 數字(integer) 年齡
politics 文字(text) 政見
votes 數字(integer),預設值 0 得票數

候選人的資料表先大概這樣設計,但其實直接把「得票數」放在這裡不是很適當,應該另外開一個資料表來記錄得票過程,但這個功能會留到後面說明 Model 關連的時候再實作會更有感覺。讓我們先使用 Rails 的產生器來建立 Model 吧:

$ bin/rails generate model candidate name party age:integer politics:text votes:integer
  Running via Spring preloader in process 10531
    invoke  active_record
    create    db/migrate/20170502075741_create_candidates.rb
    create    app/models/candidate.rb
    invoke    test_unit
    create      test/models/candidate_test.rb
    create      test/fixtures/candidates.yml

因為我們想要給得票數(votes)的預設值設定成 0,但這件事沒辦法直接從產生器的指令完成,所以請先編輯 migration 檔,在 t.integer :votes 那行後面加上 default: 0 以設定這個欄位的預設值:

  class CreateCandidates < ActiveRecord::Migration[5.0]
    def change
      create_table :candidates do |t|
        t.string :name
        t.string :party
        t.integer :age
        t.text :politics
        t.integer :votes, default: 0

        t.timestamps
      end
    end
  end

最後,別忘了把 Migration 轉換成真正的表格:

$ bin/rails db:migrate
== 20170502075741 CreateCandidates: migrating =================================
-- create_table(:candidates)
   -> 0.0012s
== 20170502075741 CreateCandidates: migrated (0.0013s) ========================

第 03 步 - 新增 View

有 Route、Controller 跟 Model 之後,接下來就要做頁面跟表單了。請先在 app/views/candidates 目錄下新增以下幾個檔案:

檔案名稱 用途
index.html.erb 全部候選人列表
new.html.erb 新增候選人資料
edit.html.erb   編輯候選人列表    

Rails 目前沒有專門為 View 設計的產生器,所以上面這幾個檔案請自己手動新增檔案。

第 04 步 -「候選人列表」功能

「候選人列表」頁面要把所有候選人的資料從資料表抓出來,然後顯示在畫面上,所以這個步驟需要動到 Controller、View 以及 Model(不過事實上 Model 在這個步驟不需要做什麼事)。這個步驟需要先修改 Controller 跟 index.html.erb 頁面,先看一下 Controller:

  # 檔案:app/controllers/candidates_controller.rb
  class CandidatesController < ApplicationController
    def index
      @candidates = Candidate.all
    end
  end

這裡使用了 Model 的 all 類別方法取得所有資料,並存成 @candidates 實體變數,以便待會在 View 可使用。

注意事項:

  1. 像這樣取出來是一批資料的,慣例上都會使用複數名詞,例如 @candidates@users
  2. 除非必要,請盡量先使用區域變數而不要使用實體變數,以上面這個例子來說,因為稍後要在 View 使用 @candidates 變數所以才使用實體變數。

接下來修改 index.html.erb

  <h1>候選人列表</h1>

  <%= link_to "新增候選人", new_candidate_path %>

  <table>
    <thead>
      <tr>
        <td>候選人姓名</td>
        <td>政黨</td>
        <td>政見</td>
        <td>得票數</td>
      </tr>
    </thead>
    <tbody>
      <% @candidates.each do |candidate| %>
      <tr>
        <td><%= candidate.name %>(年齡:<%= candidate.age %> 歲)</td>
        <td><%= candidate.party %></td>
        <td><%= candidate.politics %></td>
        <td><%= candidate.votes %></td>
      </tr>
      <% end %>
    </tbody>
  </table>

這裡有幾個地方需要說明一下:

  1. 最上面的「新增候選人」連結,雖然可以使用一般的 <a href="...">..</a> 方式來寫,但在 Rails 裡更建議使用 link_to 方法來產生連結。
  2. 中間的那段 Block 裡面的變數命名,慣例上會使用前面那個實體變數(@candidates)的單數名詞(candidate)。
  3. 你有發現我們在這個檔案(index.html.erb)只有從 <h1> 開始寫,但檢視原始碼的時候卻發現 <html><title><body> 等標籤都有了嗎? 這個其實是 Rails 裡的 Layout 做的好事,會在後面章節有更詳細介紹。

啟動 Rails Server 看一下,現在的畫面應該會長得像這樣:

image

目前因為都還沒有資料所以一片空白很正常。

第 05 步 -「新增候選人資料」功能 Part 1

這個步驟比前面幾步麻煩一點,需要修改的地方主要是 Controller 跟 View。

先編輯 app/views/candidates/new.html.erb,把新增資料的表單放在這個頁面:

  <h1>新增候選人</h1>

  <%= form_for(@candidate) do |f| %>
    <%= f.label :name, "姓名" %>
    <%= f.text_field :name %> <br />

    <%= f.label :age, "年齡" %>
    <%= f.text_field :age %> <br />

    <%= f.label :party, "政黨" %>
    <%= f.text_field :party %> <br />

    <%= f.label :politics, "政見" %>
    <%= f.text_area :politics %> <br />

    <%= f.submit %>
  <% end %>

  <br />
  <%= link_to '回候選人列表', candidates_path %>

這個 form_for 跟前個章節在介紹 BMI 計算機的 form_tag 有點類似,但更厲害一點。form_for 方法可以接一個 Model 物件參數,在這裡我們先傳一個名為 @candidate 的實體變數給它,待會在 Controller 再看看它是怎麼做出來的。在 form_for 方法後面的那個 Block 裡面的 f 變數,是一種 FormBuilder物件,可以透過這個物件上的 text_fieldtext_areasubmit 方法做出對應的 <input> 標籤。

接下來看一下 Controller:

  class CandidatesController < ApplicationController
    # .. [略] ..

    def new
      @candidate = Candidate.new
    end
  end

多加了一個 new 方法,裡面只放了簡單的一行,就是使用 Candidate 這個 Model 類別做出一個新的實體,並命名為 @candidate 實體變數,稍候供 View 使用。現在的畫面應該是這個樣子:

image

不怎麼美觀!沒關係,先把功能做出來,下個章節我們再來想辦法美化它。

在 Controller 的 new 方法裡設定的實體變數 @candidate,就是要給剛剛前面 form_for 用的。form_for 除了可以產生 <form> 標籤之外,它的 action,也就是當你按下送出按鈕要去的那個地方,會根據傳給它的這個物件是新的還是舊的而自己判斷。在這裡因為是剛剛才做出來的,form_for 會認為你現在是要做「新增」這件事。檢視一下原始碼,看一下 <form> 的那段:

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

因為它認為你是要「新增」,所以 action 的網址自動幫你設定成 /candidates,並且使用 post 方法傳送。回想一下我們目前的 Route:

$ bin/rails routes
        Prefix Verb   URI Pattern                    Controller#Action
    candidates GET    /candidates(.:format)          candidates#index
               POST   /candidates(.:format)          candidates#create
 new_candidate GET    /candidates/new(.:format)      candidates#new
edit_candidate GET    /candidates/:id/edit(.:format) candidates#edit
     candidate GET    /candidates/:id(.:format)      candidates#show
               PATCH  /candidates/:id(.:format)      candidates#update
               PUT    /candidates/:id(.:format)      candidates#update
               DELETE /candidates/:id(.:format)      candidates#destroy

/candidates 路徑使用 POST 方法,Route 會去找 candidates#create 處理。

咦?怎麼這麼巧?其實這不是巧合,這就是 Rails 的慣例。

第 06 步 -「新增候選人資料」功能 Part 2

既然知道這個表單按下送出之後會把資料拋給 Controller 的 create 方法,那就準備來「接球」吧:

  class CandidatesController < ApplicationController
    # .. [略] ..

    def create
      @candidate = Candidate.new(params[:candidate])

      if @candidate.save
        # 成功
        redirect_to candidates_path, notice: "新增候選人成功!"
      else
        # 失敗
        render :new
      end
    end
  end

傳過來的那包資料會被收集在 params 這個特別的變數裡(正確來說 params 是一個方法),透過 params[:candidate] 可以取得前一個畫面拋過來那些欄位的資料,並且把它傳給 Candidate Model 的 new 方法以建立物件。

接下來,呼叫剛剛建立的物件的 save 方法準備存檔。如果存檔成功,便轉往候選人列表頁(redirect_to),並帶有一提示訊息(Flash,後面的章節會再介紹)說「新增候選人成功!」;如果失敗,則重新 render 新增頁面,並顯示錯誤訊息。

看起來好像沒什麼問題,但當你按下新增之後會發生錯誤畫面如下:

image

這個 ActiveModel::ForbiddenAttributesError 錯誤訊息發生的原因,是因為我們試圖把 params[:candidate] 裡的資料一口氣塞進 Model 裡,這樣做會有安全上的問題,有心人士可以透過這個方式直接覆寫資料表裡某個欄位的值而取得特別權限或修改原本不應該被修改的欄位資料。

Rails 4 之後提供了一種稱之 Strong Parameters 的做法,讓你可以對這包 params 進行「清洗」或「過濾」,寫法如下:

  class CandidatesController < ApplicationController
    # .. [略] ..

    private
    def candidate_params
      params.require(:candidate).permit(:name, :age, :party, :politics)
    end
  end

方法名稱可自定,這裡我使用了 candidate_params 這個名字。因為這個方法沒有需要給外部存取,所以通常會放在 private 區塊。裡面的 permit 方法就是說「我只允許 nameage, party 以及 politics 這四個參數通過,其它的來我會無視」。因為 params[:candidates] 的內容現在只能通過你有「放行」的欄位,所以就算是被「清洗」過了。

所以,接下來把原本這行:

  @candidate = Candidate.new(params[:candidate])

改成:

  @candidate = Candidate.new(candidate_params)

因為這個 candidate_params 回傳的資料應該是「乾淨」的,所以 Rails 就可以讓你把整包資料寫進去了。試著填寫一些資料:

image

按下按鈕後應該就可以新增資料了:

image

想一想:為什麼新增資料失敗的時候是用 render 而不是用 redirect_to?

剛剛在新增資料的時候,如果新增資料成功,會轉址(redirect_to)到候選人列表頁;如果失敗,則是會執行這行:

  render :new

為什麼不是 redirect_to new_candidate_path 轉到前一頁就好了呢?

因為當使用 redirect 方式之後,等於是再次進到 /candidates/new 網址,也就是再執行了一次 new 這個 action,所有剛剛填的資料都會隨著消失;但 render :new 並不是「轉址」,而是「我還是執行 create 這個 action,只是顯示的時候借用 newview 來用」而已,不是重新執行 new 這個 action 喔,很多新手在一開始接觸 Rails 時以為 render :new 是執行 new 這個 action,這點要特別注意。

想想看如果你在填寫報稅系統,要繳稅已經不太開心了,填了一堆資料之後結果卻因為某個欄位填錯結果轉回來叫你全部欄位重填,你應該會翻桌吧。

另外,你有發現在 create 這個方法裡,剛好用了 @candidate 這個實體變數嗎:

  def create
    @candidate = Candidate.new(params[:candidate])

    if @candidate.save
      redirect_to candidates_path, notice: "新增候選人成功!"
    else
      render :new
    end
  end

正常來說,如果不需要用到實體變數,建議是只要使用區域變數就好,但因為在處理失敗狀況的 render :new,剛好在 new 的 view 有用到 @candidate 這個實體變數,所以在 create 方法裡才使用了 @candidate 實體變數,如果不考慮資料寫入失敗的狀況,是不需要使用實體變數的。在 newcreate 方法的實體變數都剛好叫做 @candidate 這個名字並不算是巧合,而是刻意使用一樣的命名,讓程式碼可以變得簡短、易讀。

第 07 步 -「編輯候選人資料」功能 Part 1

完成「新增」功能後,接下來是編輯功能,先在列表頁(index.html.erb)把「編輯」的連結加上去,「刪除」跟「投票」的連結也順便先加一下:

  <h1>候選人列表</h1>

  <%= link_to "新增候選人", new_candidate_path %>

  <table>
    <thead>
      <tr>
        <td>投票</td>
        <td>候選人姓名</td>
        <td>政黨</td>
        <td>政見</td>
        <td>得票數</td>
        <td>處理</td>
      </tr>
    </thead>
    <tbody>
      <% @candidates.each do |candidate| %>
      <tr>
        <td><%= link_to "投給這位", "#" %></td>
        <td><%= candidate.name %>(年齡:<%= candidate.age %> 歲)</td>
        <td><%= candidate.party %></td>
        <td><%= candidate.politics %></td>
        <td><%= candidate.votes %></td>
        <td>
          <%= link_to "編輯", edit_candidate_path(candidate) %>
          <%= link_to "刪除", candidate_path(candidate), method: "delete", data: { confirm: "確認刪除" } %>
        </td>
      </tr>
      <% end %>
    </tbody>
  </table>

這邊沒有太複雜的程式碼,只用了 link_to 方法加了三個連結。幾件事情說明一下:

  1. 如果不知道那個 _path 怎麼寫,請到終端機執行 rails routes 查閱 prefix 欄位。
  2. 因為投票的路徑還沒寫,所以我先把連結設定成 #
  3. link_to 的 HTTP 動詞要用對,該用 POSTDELETE 但卻忘了加的話,會造成找不到路徑的錯誤訊息。
  4. 在投票跟刪除連結後面另外加的 data: { confirm: "..." } 參數,會在做出來的連結中加上 data-confirm="..." 屬性,而 Rails 在處理這樣的連結屬性時,會跳出一個確認對話框,待使用者按下確認後才會繼續執行,避免不小心按到就直接投票或刪除了。

現在的畫面應該會長得像這樣:

image

第 08 步 -「編輯候選人資料」功能 Part 2

既然要「編輯」某一筆資料,首先要先在 Controller 裡把那筆資料抓出來:

  class CandidatesController < ApplicationController
    # .. [略] ..

    def edit
      @candidate = Candidate.find_by(id: params[:id])
    end

    # .. [略] ..
  end

edit 方法的位置不一定要在 newcreate 的前面或後面,只要找個看得順眼的地方而且不是在 private 區塊就行了。

使用 Model 的 find_by 方法,把 idparams[:id] 的資料抓出來,並把找到的結果存成實體變數 @candidate

這個 params[:id] 是什麼?先讓我們執行 rails routes 來看一下:

$ bin/rails routes
        Prefix Verb   URI Pattern                    Controller#Action
.. [略]..
 new_candidate GET    /candidates/new(.:format)      candidates#new
edit_candidate GET    /candidates/:id/edit(.:format) candidates#edit
     candidate GET    /candidates/:id(.:format)      candidates#show
               PATCH  /candidates/:id(.:format)      candidates#update
               PUT    /candidates/:id(.:format)      candidates#update
               DELETE /candidates/:id(.:format)      candidates#destroy

在候選人列表頁,當你點下編輯之後,它的網址會變成像是 /candidates/1/edit,跟 Route 比對之後就可以發現,中間的那個數字 1 就是在 Route 路徑裡的 :id,它會被捕捉在 params 裡,使用 params[:id] 就可以調出來使用。

注意:透過 params 取得的所有資料型態都是字串,包括看起來像是數字的 params[:id] 也是字串,如果是要直接拿來做數學運算的話,請記得先使用 to_ito_f 方法轉換型態。

接著,編輯 app/views/candidates/edit.html.erb 頁面:

  <h1>編輯候選人</h1>

  <%= form_for(@candidate) do |f| %>
    <%= f.label :name, "姓名" %>
    <%= f.text_field :name %> <br />

    <%= f.label :age, "年齡" %>
    <%= f.text_field :age %> <br />

    <%= f.label :party, "政黨" %>
    <%= f.text_field :party %> <br />

    <%= f.label :politics, "政見" %>
    <%= f.text_area :politics %> <br />

    <%= f.submit %>
  <% end %>

  <br />
  <%= link_to '回候選人列表', candidates_path %>

咦?等等!各位有發現這段程式碼的內容怎麼跟新增頁面的程式碼有九成像?其實這就是 form_for 方法神奇的地方。因為 form_for 發現傳進來的那顆 @candidate 物件是舊的(就是從資料庫裡調出來的),它會認定你是準備要「編輯」,所以不只 <form> 的 Action 網址會幫你依照慣例設定好,連值也會自動幫你帶進表單裡。

這時候的畫面長這樣:

image

但這個程式碼跟新增的實在是太像了,所以我們可以把重複的地方抽出來,存在另一個檔案。請手動新增一個叫做 _form.html.erb 的檔案,放在 app/views/candidates/ 裡,內容如下:

  <%= form_for(candidate) do |f| %>
    <%= f.label :name, "姓名" %>
    <%= f.text_field :name %> <br />

    <%= f.label :age, "年齡" %>
    <%= f.text_field :age %> <br />

    <%= f.label :party, "政黨" %>
    <%= f.text_field :party %> <br />

    <%= f.label :politics, "政見" %>
    <%= f.text_area :politics %> <br />

    <%= f.submit %>
  <% end %>

注意在抽出來變成的內容中,把實體變數(也就是 @ 開頭的變數)換成區域變數,原因待會會說明。原本「新增」的頁面 app/views/candidates/new.html.erb 的內容可改成這樣:

  <h1>新增候選人</h1>

  <%= render "form", candidate: @candidate %>

  <br />
  <%= link_to '回候選人列表', candidates_path %>

「編輯」的頁面 app/views/candidates/edit.html.erb 的內容也可改成這樣:

  <h1>編輯候選人</h1>

  <%= render "form", candidate: @candidate %>

  <br />
  <%= link_to '回候選人列表', candidates_path %>

現在「新增」跟「編輯」這兩個頁面都變得短短的二、三行就搞定,把共同的內容都放到 _form 裡面了。這個技巧在 Rails 稱之局部渲染(Partial Render),通常會用來整理重複的程式碼。檔名不一定要叫 _form,你也可以取做 _abcdefg,但要用的時候就要改寫成 <%= render "abcdefg" %>

注意:這個 Partial Render 的檔案名稱必須要是底線開頭,不然會發生找不到的檔案錯誤訊息。

想一想:為什麼要把局部渲染的實體變數抽掉?

在剛才使用局部渲染手法整理程式碼的時候,抽出程式碼到 partial file 的時候,大可使用剪下、貼上的手法,像這樣:

  <%= form_for(@candidate) do |f| %>
    <%= f.label :name, "姓名" %>
    <%= f.text_field :name %> <br />
    ...[略]...
  <% end %>

然後在使用的時候只要這樣:

  <%= render "form" %>

_form.html.erb 直接裡取用空中的實體變數 @candidate,使用起來簡單又方便!但為什麼不建議這麼做呢?

因為使用這樣的寫法,_form.html.erb 就會變成依賴這個命名為 @candidate 的實體變數才能正常運作。這不算是個好設計,因為這樣一來在產生這個實體變數的時候,就只能叫做 @candidate 這個名字了。

較建議的做法,是讓這個檔案設計成「被動」或「被餵食」,而達到「解耦」(decoupling)的效果,像是這樣:

  <%= form_for(candidate) do |f| %>
    <%= f.label :name, "姓名" %>
    <%= f.text_field :name %> <br />
    ...[略]...
    <%= f.submit %>
  <% end %>

簡單的說,就是 partial file 裡不要放實體變數,僅使用區域變數。以這個例子來說,_form.html.erb 這個檔案只期待有一個叫做 candidate 的區域變數,在使用它的時候,可透過 render 方法的時候傳額外的參數傳給它:

  <%= render "form", candidate: @candidate %>

萬一在別的地方也要使用這個 partial file,但實體變數名稱叫做 @qualified_candidate

  <%= render "form", candidate: @qualified_candidate %>

_form.html.erb 只要外面有傳一個 candidate 變數給它,它就可以正常運作了。在第 15 章有關於 View 跟 Layout 的介紹,也會針對這個主題再補充說明。

第 09 步 -「編輯候選人資料」功能 Part 3

根據 Route 的路徑對照表,當對 /candidates/:id 網址以 PUTPATCH 方式傳送時,會觸發 CandidatesController 上的 update 方法,所以接下來我們把這個功能給補上去:

  class CandidatesController < ApplicationController
    # ..[略]..

    def update
      @candidate = Candidate.find_by(id: params[:id])

      if @candidate.update(candidate_params)
        # 成功
        redirect_to candidates_path, notice: "資料更新成功!"
      else
        # 失敗
        render :edit
      end
    end

    # ..[略]..
  end

update 方法寫起來其實也跟 new 有點像,差別在於這邊是使用 update 方法(或使用 update_attributes 方法也可)更新資料。同樣的,為了安全考量,如果 update 方法是直接丟沒有清洗過的 params[:candidate] 給它,也一樣會發生錯誤訊息。若編輯更新成功,將會轉往候選人列表頁面,若失敗則重新 redner 編輯頁面。

到這裡,編輯功能應該就完成了!

第 10 步 -「刪除候選人資料」功能

跟「新增」或「編輯」功能比起來,刪除功能算是相對簡單的,只要把資料抓出來就可以直接刪除了。根據 Route 的路徑,對 /candidates/:id 網址以 DELETE 發送資料時,會觸發 CandidatesController 的 destroy 方法,所以就讓我們把 destroy 方法加上去:

  class CandidatesController < ApplicationController
    # .. [略] ..

    def destroy
      @candidate = Candidate.find_by(id: params[:id])
      @candidate.destroy if @candidate
      redirect_to candidates_path, notice: "候選人資料已刪除!"
    end

    # .. [略] ..
  end

這樣一樣使用 Model 的 find_by 方法,把要刪除的資料抓出來,然後呼叫那個物件的 destroy 方法,這筆資料就會刪掉了。

小結

到這裡,我們已經可以不使用內建的 Scaffold 產生器而完成候選人資料的新增、修改、刪除功能了。不過目前的程式碼寫得有點醜,而且不少地方是重複的,下一個章節將介紹怎麼幫它加上投票功能以及整理重複的程式碼。

以上實作完整程式碼可在 https://github.com/kaochenlong/my_candidates 取得。

← 上一章:Controller 下一章:CRUD 分解動作 - 簡易票選系統實作(下) →

Comments