練習 - 014 (投票記錄表)
利用 Rails 的 Model 產生器,產生一個資料表出來,並且幫我們做好 migration。
需要的欄位有 ↓
candidate_id:因為是一個參照值,可以使用
candidate:references
這個指令,Rails會幫我們去撈candidate的流水編號(id)欄位。ip_address:想要有記名投票的效果,所以我們需要投票人的ip位置。資料型態雖然是 string 但是這邊可以省略不寫:string。
在終端機輸入指令。
可以看到終端機顯示的資訊,有產生migration檔案也同時有建立model。
確認model目錄底下有建議了 VoteLog 這個 model,並且幫我們設定好belongs_to :candidate
再到db目錄底下看到有新增了 migration 檔案,確認檔案內容是否都正確。
確認migration檔案內容都沒問題,接著我們就可以把它「具現化」。
終端機執行 rials db:migrate
指令,終端機畫面顯示有migrate成功。
那我們現在都有投票紀錄了,接著就是要把它寫進去,因此我們回到CandidatesController
在vote action
裡面增加
1 | VoteLog.new(candidate: @candidate, ip_address: ???) |
這遇到ip位置不知道該放什麼value
給它,這時候我們可以請出google大神,輸入關鍵字rails ip_address
,通常前幾篇就會找到需要的答案,很幸運的我們很快就找到了。request.remote_ip
1 | VoteLog.new(candidate: @candidate, ip_address: request.remote_ip) |
但是如果是用new方法的話,會需要給它一個變數承接,在執行.save
1 | v = VoteLog.new(candidate: @candidate, ip_address: request.remote_ip) |
這裡我們可以換另外一種寫法,比較直接一點,不需要兩段式的方式寫入資料庫。create
的方法可以直接存進去。
1 | VoteLog.create(candidate: @candidate, ip_address: request.remote_ip) |
打開瀏覽器,來投票試試看,先記下2號候選人ddd的目前票數2票。
接著按下左邊的vote
,來觀察一下是否有變化。變成3票了!!ˊ而且畫面也有顯示「投票成功!!!」
確認有沒有確實寫入資料庫,透過rails console
來確認。
輸入指令VoteLog.all
就可以看到目前在VoteLog
model裡面有多少的投票紀錄。
這樣每個候選人(id)被點擊一次vote
就會被記錄下來。
此時我們可以換個角度從候選人角度來寫,候選人名字有被投票的就會被紀錄起來。
另外原本下面的第55-56行寫的程式碼,是針對候選人的某個欄位執行遞增,並且將結結果save到資料庫中。
因為我們現在已經寫了第53行的程式碼,所以55-56行的程式碼就可以拿掉。
打開瀏覽器,此時按下vote
會顯示錯誤訊息「沒有定義vote_logs
方法」
為什麼會看到,是因為剛剛我們給每一位候選人都有很多「投票紀錄」model,透過這個「投票紀錄」model,直接建立一個投票紀錄出來。這樣我們就可以計算票數。
雖然我們剛剛建立VoteLog Model
時有使用references:candidate
這個指令,會幫我們在vote_log.rb
這個model檔案直接幫我們加上belongs_to :candidate
。
但是相對的Candidate Model
卻沒有幫我們做這件事情。
因為Rails不曉得我們是要做「一對一」關聯?「一對多」關聯?它沒有辦法去猜,所以這邊就只能由我們自己手動來幫Model們來建立「關聯」。
進到Candidate Model
手動新增has_many :vote_logs
,要記得log是負數型態(logs)。
關於has_many
的黑魔法,之後會有另位的章節介紹它。目前先知道它其實默默地在背後建立一個叫做vote_logs
的方法。
這邊我們回到瀏覽器,回到前一頁,先確認目前每位候選人的票數。
對候選人eee
按下vote
,再看看是否會一樣出現錯誤訊息。
有顯示投票成功,可是票數還沒有改變一樣維持4票。這時候我們可以透過rails console
來觀察一下是否真的有投票成功。
輸入指令:VoteLog.count
,看到顯示有7票。
再繼續對eee
候選人多按3次vote
,看看數字會不會變化:
確實現在票數變成10票了。是有成功將票數寫進資料庫中。但是目前瀏覽器的畫面依然顯示票數是4票
會有這樣的原因是原本我們把票數的累進,寫在了Candidate Model
的votes
欄位累進票數。不是現在我們使用的VoteLog Model
。
所以我們進到view/index.html.erb
檔案來修改一下<%= candidate.votes %>
的內容,改成<%= candidate.vote_logs %>
。
因為每個候選人都會有很多投票紀錄,所以<%= candidate.vote_logs %>
會回傳一大包東西 (是一個陣列)。
可以透過rails console
來看一下回傳的資料內容,首先我們先宣告一個c1
變數=候選人的第一個c1 = Candidate.first
輸入c1.vote_logs
説:「誒,聽說你有很多票。」會看到一長串長得像陣列的東西。
再輸入c1.vote_logs.count
説:「那你到底有幾張票」。c1候選人告訴你:「我有2張票。」
所以這時候我們可以回到view/index.html.erb
再修改剛剛的那行程式碼<%= candidate.vote_logs.count %>
。這樣應該就會拿到我們想要看到的票數。
再回到瀏覽器重新整理,此時可以發現票數改變了。變成了votelogs
紀錄的票數。
這時候我們對ccc
候選人再投5票。看看是否票數會增加變成7。
真的有成功投票,並且寫進資料庫。
如果要確認投票紀錄,現在我們可以再透過rails console
來觀察
一樣輸入:c1 = Candidate.first
、c1.vote_logs
。
可以看到一號候選人的投票紀錄:
雖然有顯示投票紀錄了。但是不知道有沒有發現一件事情,執行rails console
如果要看去投票紀錄。輸入c1.vote_logs
它是會撈後選人的資料,你有幾票它會去撈幾次。
這個狀況是否似成相似?有沒有「N + 1」的感覺。如果候選人很多時,大家同時上線投票,會很佔用資源,有資源消耗的問題。
目前已經看到每次投票的紀錄,但是其實有需要優化的地方,「效能」。
下一堂課程,改善效能。
參考來源:為你自己學 Ruby on Rails (https://railsbook.tw/)