iT邦幫忙

2021 iThome 鐵人賽

DAY 18
0
自我挑戰組

Ruby on Rails JS系列 第 18

Ruby on Rails CRUD 之 R(Read)

CRUD 之 R(Read)
從資料表裡讀取資料也是很常⾒的操作,在讀取的⽅法就比寫入來得多樣化,有⼀
次讀取⼀筆的⽅法,也有⼀次讀取⼀整批的⽅法。
first & last
想要取得資料表中的第⼀筆或最後⼀筆資料,可使⽤ first 或 last ⽅法:

user = Candidate.first # 取得第 1 筆資料
users = Candidate.first(3) # 取出前 3 筆資料並存放在陣列裡

要注意的是, first 本⾝只會取出 1 筆資料,⽽ first(3) 雖然是取出前 3 筆
資料,但會放在⼀個陣列裡。Model 預設使⽤ id 流⽔編號做為排序,所以如果想
要反過來,可以⽤連續技:

Candidate.order(age: :desc).first # 取出年紀最⼤的候選⼈

find & find_by
如果想要找到指定 id 的候選⼈,可使⽤ find 或 find_by ⽅法:

Candidate.find(1)
Candidate.find_by(id: 1)

這兩種⽅式都可以找到編號是 1 號的候選⼈。差別是 find_by ⽅法如果找不到指
定 id 的資料,僅會回傳 nil 物件,但 find ⽅法會直接產⽣
ActiveRecord::RecordNotFound 的例外訊息,例如搜尋⼀個不存在的候選⼈編
號 9487 :

>> Candidate.find_by(id: 9487)
Candidate Load (0.1ms) SELECT "candidates".* FROM "candidates
" WHERE "candidates"."id" = ? LIMIT ? [["id", 9487], ["LIMIT", 1
]]
=> nil
>> Candidate.find(9487)
Candidate Load (0.2ms) SELECT "candidates".* FROM "candidates
" WHERE "candidates"."id" = ? LIMIT ? [["id", 9487], ["LIMIT", 1
]]
ActiveRecord::RecordNotFound: Couldn't find Candidate with 'id'=9
487

...[略]...
但要怎麼處理 ActiveRecord::RecordNotFound 這個例外訊息? 有以下幾種做
法:

  1. 在發⽣的問題點解決它:
    以第 14 章的例⼦為例:
class CandidatesController < ApplicationController
# .. [略] ..
private
def find_candidate
@candidate = Candidate.find_by(id: params[:id])
end
end

在該章節的範例中我們是使⽤ find_by ⽅法查詢,如果查不到僅是回傳 nil 物件,
但如果改⽤ find ⽅法,在查不到資料的時候就會發⽣例外訊息,這時候就可以在
發⽣錯誤的地⽅使⽤ Ruby 內建的 begin .. rescue ⽅法來捕捉它:

def find_candidate
begin
@candidate = Candidate.find(params[:id])
rescue
redirect_to candidates_path, notice: "查無此候選⼈"
end
end

當 find ⽅法查不到資料、發⽣例外訊息,就會進⾏ rescue 路線。如果沒有其
它額外的邏輯判斷或流程,可以把上⾯這段範例再簡化成這樣:

def find_candidate
@candidate = Candidate.find(params[:id])
16 Model 基本操作
246
rescue
redirect_to candidates_path, notice: "查無此候選⼈"
end
  1. 在 Controller 層級解決它:
    因為所有的 Controller 預設都是繼承 ApplicationController ,所以也可在
    ApplicationController 上使⽤ rescue_from ⽅法來捕捉例外訊息,像這
    樣:
class ApplicationController < ActionController::Base
# ... [略] ...
rescue_from ActiveRecord::RecordNotFound, with: :record_not_fou
nd
private
def record_not_found
render plain: "查無資料", status: 404
end
end

這樣⼀來只要在所有的 Controller 發⽣ ActiveRecord::RecordNotFound 例外,
就會在畫⾯上印出⼀個「查無資料」的字樣,並且設定 HTTP 狀態為 404。
all, where, order, limit
使⽤ all ⽅法可取得所有資料,取得資料將存放在陣列裡:
Candidate.all
where ⽅法則會再加上⼀些條件做為篩選:

Candidate.where("age > 18", gender: "female")

這樣可取得「女性且⼤於 18 歲」的候選⼈。但如果要分開兩段寫也是可以:

Candidate.where("age > 18").where(gender: "female")

結果也是⼀樣的。另外,使⽤ order ⽅法可對資料做排序:

Candidate.order(:age) # 按照年齡⼤⼩,預設是由⼩排到⼤
Candidate.order(age: :desc) # 按照年齡⼤⼩,由⼤排到⼩

如果想要限制取得筆數,則是使⽤ limit ⽅法:

Candidate.order(age: :desc).limit(3)

這樣即可取得「年齡最⼤的三個候選⼈」。
count, average, sum, maximum, minimum
想要知道總共有多少筆數,可使⽤ count ⽅法:

$ bin/rails console
>> Candidate.count
(0.1ms) SELECT COUNT(*) FROM "candidates"
=> 3

如果想要算資料的「總和」或「平均」,很多新⼿ Rails ⼯程師會先想到的可能是
「把全部資料⽤ all ⽅法抓出來,然後跑 each 迴圈來計算總和及平均」,但
這其實是不好的做法,不僅速度慢⼜浪費系統資源。像這種總和或平均值的計算,
⼤部份的資料庫系統本⾝都有直接⽀援這個功能,千萬不要傻傻的抓出來⾃⼰跑迴
圈算:

$ bin/rails console
>> Candidate.sum(:age)
(0.2ms) SELECT SUM("candidates"."age") FROM "candidates"
=> 44
>> Candidate.average(:age).to_f
(0.1ms) SELECT AVG("candidates"."age") FROM "candidates"
=> 14.6666666666667

使⽤ sum 或 average ⽅法就可以請資料庫直接幫我們做計算。
最⼤值跟最⼩值也是⼀樣,千萬不要傻傻的⽤ all 全部抓出來再跑迴圈或寫什麼
排序演算法,直接使⽤ maximum 或 minimum ⽅法請資料庫幫你算就好:

$ bin/rails console
>> Candidate.maximum(:age)
(0.2ms) SELECT MAX("candidates"."age") FROM "candidates"
=> 22
>> Candidate.minimum(:age)
(0.2ms) SELECT MIN("candidates"."age") FROM "candidates"
=> 2

參考資料

[為你自己學Ruby on Rails]https://railsbook.tw/chapters/08-ruby-basic-4.html


上一篇
Ruby on Rails Model 基本操作之 CRUD CRUD 之 C(Create)
下一篇
Ruby on Rails CRUD 之 U(Update)
系列文
Ruby on Rails JS29

尚未有邦友留言

立即登入留言