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


<<:  每个人都该学的30个Python技巧|技巧 18:Python容器—字典(dictionary)(字幕、衬乐、练习)

>>:  Day 17 读 Go Concurrency Patterns - Rob Pike IV

JS Library 学习笔记:嘿!有听过 GSAP 吗? (四)

在gsap.timeline()上使用如to()、from()或fromTo()等函式建立动画,除了...

Backtrader - 自订 datafeeds

我们之前在喂历史资料,都是先用 shioaji 下载下来,然後再用 padas 转成 datafra...

Icebear的参赛心得

参赛心得 其实会参加这个比赛是学校老师出的一个自学作业,完全想不到到了大三还会有暑假作业,在开赛之前...

DAY5 - Side Project 主题:90天原子习惯挑战

荀子劝学篇中有一段是这样的: 「积土成山,风雨兴焉;积水成渊,蛟龙生焉;.....。故不积蹞步,无以...

D23 - 用 Swift 和公开资讯,打造投资理财的 Apps { 台股成交量实作.3 }

在 KLineViewController 开出的 volumeDataSet 会在 parent ...