跳至主要内容

[Rails] Active Record Basic(Model 基本的 CRUD 操作)

速記

  • where 取出來的資料會是陣列

目錄

[TOC]

觀念

Active Record 在 Rails 中負責的 Model 部分,主要負責處理商業邏輯和資料。每個資料表(table)大部分都能對應到一個 Rails 中的 Model Class,但是 Model Class 並不是真正的資料表,而是一個負責跟實體的資料表溝通的抽象類別。

Object Relational Mapping(ORM)則是將應用程式中的許多物件和關連性資料庫中的資料表做結合,透過 ORM 可以更容易以物件的方式存取資料庫中的資料而不需直接撰寫 SQL。

-Active Record Basic @ RailsGuides -Ruby on Rails 命名慣例整理

套用 Active Record Model(ApplicationRecord)

只要讓 Model 的 Class 繼承 ApplicationRecord

class User < ApplicationRecord
end

就可以使用基本操作 Model 的方法:

new_user = User.new
new_user.name = 'Aaron'
new_user.save

Console

進入 rails console 後可以使用下面 CRUD 的指令來操作資料庫:

bin/rails c

Create

keywords: create, new

可以使用 Model class method 中的 createnew 來建立一個新的實例。

new 跟 create 的差別,就是 new 方法只是先把物件做出來,尚未存入資料表,因此要手動透過 save 儲存;而 create 方法則是直接把存入資料表

不管是 new 或 create,在寫入失敗的時候都會回傳一個「內容都是 nil 的 Model 物件」,通常在 Controller 的 Action 裡常會藉此判定資料寫入是否成功。

如果希望在 create 寫敗時就回拋錯誤訊息,可以使用 create!

user = User.create(name: 'Aaron', email: 'aaronchen@wavinfo.com', age: 29)  # 直接儲存
user = User.new(name: 'PJCHENder', email: 'pjchender@gmail.com', age: 27) # 建立物件實例但尚未儲存

# 一次建立多筆 record
reviewers = User.create([{name: 'Aaron'}, {name: 'Andyyou', email: 'aaronchen@wavinfo.com', age: 29})

程式範例

new

記得最後要用 save 後才會寫入 database:

#####################################################
## 使用 new 方法 - 需要 save ##
#####################################################

# 備註:該物件的 id 值一開始是 nil ,寫入 save 後才會自動帶值,
# 如果寫入的 id 的值不存在,後面新增的資料 id 會依序最大的 id 往後排
# 如果寫入的 id 值已經存在過,則 save 會出現錯誤

# 直接將要設定的值代入在 new 當中
candidate = Candidate.new(party: "綠黨", politics: "多蓋大樹")
post.save
# 也可以一步一步做

post = Post.new # 建立一個空資料(一列空資料)
# => <Post id: nil, title: nil, created_at: nil, updated_at: nil>
post.title = "Hello everyone!" # 設定 title 的值
# => "Hello everyone!"
post # 輸入 post 會回傳現在該物件的內容
# => #<Post id: nil, title: "Hello everyone!", created_at: nil, updated_at: nil>
post.save # 將物件的資料寫入資料庫中,要記得是 instance object 的 save method(小寫)
# => true

create

不需要用 save 就會寫入 database:

# create 方法有另一個叫做 create! 的方法,兩者差別是當寫入發生錯誤時,create! 會產生例外(Exception)訊息。
Candidate.create(name: "馬應九", party: "綠黨", age: 32, politics: "多蓋大樹") # 沒有填的欄位,若沒有預設值,會填入 nil

Read and Filter

keywords: all, includes, limit, find, find_by, where, order, reorder
User.all
Post.includes(:user) # 透過 IN 的方式進行 SQL,節省效能

User.first # 取得 User 資料表的第一筆資料
User.first(3) # 取得 User 資料表的前三筆資料
User.limit(3) # 限制取得的資料筆數
User.last # 取得 User 資料表的最末筆資料

User.find(3) # 找出資料表 User 中 id 為 3 的資料,找不到時會拋錯誤,效能較好
User.find_by(name: 'Aaron') # 根據某欄位進行尋找,找不到時回傳 nil

User.where(name: 'David', occupation: 'Code Artist')
User.where("age > 18", "vote > 3")
User.where.not("age > 18", "vote > 3") # 不要取某些值

User.order(:age) # 按照年齡排序,預設由小排到大
User.order(age: :desc) # 按照年齡排序,由大到小
User.order(:age).reorder(age: :desc) # 如果之前已經有 order 過要再重新排序,需要下 reorder

可以使用方法鍊的方式:

Candidate.where("age > 18")
.where(gender: "female")
.limit(2)
.order(age: :desc)

Post.order(age: :desc).first # 先排序在找出第一個(找出年紀最大者)

不需要擔心連續兩次的 where 方法或是這樣一直連下去會造成多次的查詢,事實上 Rails 在處理這行語法的時候,是先整行語法解讀完才向資料庫進行查詢。

Active Record Query Interface @ RailsGuides

Update

keywords: save, update, update_attribute, update_attributes, Model.update_all
  • save:預設會經過驗證(Validation,在稍後的章節會介紹)流程,如果驗證失敗將無法寫入。如果想要跳過驗證,可加上 validates: false 參數。
  • updateupdate_attributes 其實只是名字不一樣,但事實上是一模一樣的內容。
  • update_attribute 方法會跳過驗證(Validation),等於是 save(validate: false) 的效果。
  • save!, update! 會在資料無法驗證存入時回拋例外錯誤。
user.name = 'Aaron'
user.save # 使用 user.save! 當儲存失敗(或驗證失敗)時會拋出錯誤

user.update(name: 'Aaron') # 使用 user.update! 當儲存失敗(或驗證失敗)時會拋出錯誤

# 一次更新多筆資料的多個欄位
User.update_all "max_login_attempts = 3, must_change_password = 'true'"

程式範例

# 先找出想要更新的對象
post = Post.find(5) # 根據 id 選擇想要更新的資料
# => <Post id: 5, title: "Set id manually", created_at: "2017-02-17 09:16:15", updated_at: "2017-02-17 09:16:15">

儲存資料(save)

# 修改欄位值
post.title = "I want to be updated" # 設定要更新的欄位值
=> "I want to be updated"
post.save # 寫入資料庫
=> true
Post.find(5)
=> #<Post id: 5, title: "I want to be updated", created_at: "2017-02-17 09:16:15", updated_at: "2017-02-17 09:33:14">

以不驗證資料的方式儲存(validate: false)

order.save(validate: false)

更新多個欄位(update, update_attributes)

candidate.update_attributes(name: "陳橘", age: 54)
candidate.update(name: "陳橘", age: 54)

更新單一欄位資料(update_attribute)

不需要在呼叫 save

candidate.update_attribute(:name, "賴神")

更新整個資料表(Model.update_all)

# 這是類別方法(class method)
Candidate.update_all(vote: 0)

重新載入資料表(reload)

order = Order.first
order.reload

Delete, Destroy

keywords: delete, destroy, Model.destroy_all

destroydelete 的差別,在於 destroy 方法在執行的時候,會執行完整的回呼(Callbacks),但 delete 方法僅直接執行 SQL 的 delete from ... 語法,不會觸發任何回呼。

Wavinfo 開發上習慣用 destroy

instance method

user.destroy
user.delete

class method

Candidate.destroy(1)       # 刪除編號 1 的資料
Candidate.delete(1) # 刪除編號 1 的資料

Candidate.destroy_all # 刪除資料表所有資料
Candidate.destroy_all("age < 18") # 刪除所有年齡小於 18 的資料

參考資料