接著上一篇我們繼續說明用 ORM 來操作 CRUD 的部分 關於讀取的部分還可以怎麼去找到,還有後面的 update 及 delete 的說明。

更精準的讀取 find & find_by & Where

find & find_by

用 find 來找 id
Model.find(:id)
find_by 可以找特定欄位的資訊是否有對應的資料
Model.find_by(欄位名: 值)

兩者的差異:

  • find 只能找 id,find_by 可以找任何資料表內的東西
  • find 找不到會出錯, find_by 只會回傳 nil
1
2
3
4
5
6
7
8
9
10
11
3.0.0 :001 > a1 = Article.find(1)
Article Load (0.2ms) SELECT "articles".* FROM "articles" WHERE "articles"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
=> #<Article id: 1, title: "文章1", content: "進入ruby世界說hello world", created_at: "2022-10-03 12:41:21.8330...

3.0.0 :002 > a2 = Article.find_by(id: 2)
Article Load (0.3ms) SELECT "articles".* FROM "articles" WHERE "articles"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]]
=> #<Article id: 2, title: "文章2", content: "CRUD是什麼?", created_at: "2022-10-04 09:31:49.206583000 +0000",...

3.0.0 :003 > a2 = Article.find_by(title: "文章2")
Article Load (0.3ms) SELECT "articles".* FROM "articles" WHERE "articles"."title" = ? LIMIT ? [["title", "文章2"], ["LIMIT", 1]]
=> #<Article id: 2, title: "文章2", content: "CRUD是什麼?", created_at: "2022-10-04 09:31:49.206583000 +0000",...

找不到資料時的差別

1
2
3
4
5
6
7
8
9
10
4.3.0.0 :001 > a3 = Article.find_by(id: 3)
Article Load (0.3ms) SELECT "articles".* FROM "articles" WHERE "articles"."id" = ? LIMIT ? [["id", 3], ["LIMIT", 1]]
=> nil

4.3.0.0 :002 > a3 = Article.find(3)
Article Load (0.3ms) SELECT "articles".* FROM "articles" WHERE "articles"."id" = ? LIMIT ? [["id", 3], ["LIMIT", 1]]
Traceback (most recent call last):
1: from (irb):13:in `<main>'
ActiveRecord::RecordNotFound (Couldn't find Article with 'id'=3)
3.0.0 :014 >

where

where 可以搭配一些篩選條件,比如過 id > 1,內容包含某些資料之類的條件等等,

我們再建立第 3 筆文章資料,然後透過 where 篩選 id > 1 看看吧。

1
2
3
4
5
6
7
3.0.0 :001 > Article.all
Article Load (0.3ms) SELECT "articles".* FROM "articles" /* loading for inspect */ LIMIT ? [["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<Article id: 1, title: "文章1", content: "進入ruby世界說hello world", created_at: "2022-10-03 12:41:21.833091000 +0000", updated_at: "2022-10-03 12:41:21.833091000 +0000">, #<Article id: 2, title: "文章2", content: "CRUD是什麼?", created_at: "2022-10-04 09:31:49.206583000 +0000", updated_at: "2022-10-04 09:31:49.206583000 +0000">, #<Article id: 3, title: "文章3", content: "MVC是什麼?", created_at: "2022-10-04 10:10:05.905998000 +0000", updated_at: "2022-10-04 10:10:05.905998000 +0000">]>

4.3.0.0 :002 > Article.where("id > 1")
Article Load (0.4ms) SELECT "articles".* FROM "articles" WHERE (id > 1) /* loading for inspect */ LIMIT ? [["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<Article id: 2, title: "文章2", content: "CRUD是什麼?", created_at: "2022-10-04 09:31:49.206583000 +0000", updated_at: "2022-10-04 09:31:49.206583000 +0000">, #<Article id: 3, title: "文章3", content: "MVC是什麼?", created_at: "2022-10-04 10:10:05.905998000 +0000", updated_at: "2022-10-04 10:10:05.905998000 +0000">]>

可以看到根據條件就撈出 id=2、id=3 兩篇文章的資料。

Update 更新

用 save 更新

先找到要的資料,然後修改欄位數值,再用 save 更新

1
2
3
a1 = Article.find_by(id: 1)
a1.title = "第一篇文章"
a1.save

使用 update_attribute

只更新單一欄位的值 (attribute 是單數),注意會跳過驗證

1
a1.update_attribute(:title, "第一篇文章")

使用 update & update_attributes

兩個方法只是名稱不同,都可一次更新多個欄位,且不需要再呼叫 save 方法

1
2
a1.update(title: "第一篇文章", content: "你知道要怎麼印出'hello world'嗎?")
a1.update_attributes(title: "第一篇文章", content: "你知道要怎麼印出'hello world'嗎?")

實際在 console 裡面操作的情形

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
3.0.0 :001 > a1 = Article.find(1)
(0.4ms) SELECT sqlite_version(*)
Article Load (0.1ms) SELECT "articles".* FROM "articles" WHERE "articles"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
=> #<Article id: 1, title: "文章1", content: "進入ruby世界說hello world", created_at: "2022-10-03 12:41:21.8330...

3.0.0 :002 > a1.update_attribute(:title, "第一篇文章")
TRANSACTION (0.1ms) begin transaction
Article Update (0.7ms) UPDATE "articles" SET "title" = ?, "updated_at" = ? WHERE "articles"."id" = ? [["title", "第一篇文章"], ["updated_at", "2022-10-04 10:20:44.717780"], ["id", 1]]
TRANSACTION (0.9ms) commit transaction
=> true

3.0.0 :003 > a1.title = "改回文章1"
=> "改回文章1"

3.0.0 :004 > a1.save
TRANSACTION (0.1ms) begin transaction
Article Update (0.7ms) UPDATE "articles" SET "title" = ?, "updated_at" = ? WHERE "articles"."id" = ? [["title", "改回文章1"], ["updated_at", "2022-10-04 10:21:31.485894"], ["id", 1]]
TRANSACTION (1.0ms) commit transaction
=> true

Delete 刪除

刪除有 deletedestroy 兩種方法,可以將資料刪除。

1
2
3
4
5
6
7
a1 = Article.find_by(id: 1)
a1.destroy
a1.delete

## 等同於從 model 直接找id進行刪除
Article.destroy(1)
Article.delete(1)

終端機操作的顯示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
3.0.0 :001 > a1 = Article.find(1)
Article Load (0.3ms) SELECT "articles".* FROM "articles" WHERE "articles"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
=> #<Article id: 1, title: "改回文章1", content: "進入ruby世界說hello world", created_at: "2022-10-03 12:41:21....

3.0.0 :002 > a1.destroy
TRANSACTION (0.1ms) begin transaction
Article Destroy (0.7ms) DELETE FROM "articles" WHERE "articles"."id" = ? [["id", 1]]
TRANSACTION (0.9ms) commit transaction
=> #<Article id: 1, title: "改回文章1", content: "進入ruby世界說hello world", created_at: "2022-10-03 12:41:21.833091000 +0000", updated_at: "2022-10-04 10:21:31.485894000 +0000">

3.0.0 :003 > Article.delete(2)
Article Destroy (1.9ms) DELETE FROM "articles" WHERE "articles"."id" = ? [["id", 2]]
=> 1

3.0.0 :009 > a2 = Article.find(2)
Article Load (0.3ms) SELECT "articles".* FROM "articles" WHERE "articles"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]]
Traceback (most recent call last):
1: from (irb):9:in `<main>'
ActiveRecord::RecordNotFound (Couldn't find Article with 'id'=2)

destroy 跟 delete 的差別:

  • destroy 方法執行的時候,會執行完整的回呼(Callback)
  • delete 方法直接執行 SQL 的 delete from 語法,不會進行任何回呼。

參考資料:

  1. Rails Guide
  2. 為你自己學 Ruby on Rails