Stories

Detail Return Return

Callback詳解 - Stories Detail

Callbacks

Callback Registration

在 Rails 中,回調(Callbacks)是一種在模型對象的生命週期中執行特定代碼的機制。回調可以在模型對象的創建、更新、刪除等操作中執行特定的代碼,例如保存對象前執行某些邏輯,或者在對象被刪除前執行清理操作。

Rails 中的回調分為兩種類型:前置回調(before callbacks)和後置回調(after callbacks)。前置回調在操作執行之前執行,後置回調在操作執行之後執行。可以使用 before_after_ 前綴來指定回調的類型。

以下是一些常見的回調類型:

  • before_validationafter_validation:在驗證對象之前和之後執行回調。
  • before_saveafter_save:在保存對象之前和之後執行回調。
  • before_createafter_create:在創建對象之前和之後執行回調。
  • before_updateafter_update:在更新對象之前和之後執行回調。
  • before_destroyafter_destroy:在刪除對象之前和之後執行回調。

要註冊回調,可以在模型類中使用 before_after_ 前綴來指定回調的類型,然後指定要執行的方法:

class User < ApplicationRecord
  before_save :normalize_email

  private

  def normalize_email
    self.email = email.downcase
  end
end

在上面的例子中,我們將 normalize_email 方法註冊為 before_save 回調。這意味着在保存 User 對象之前,Rails 將自動調用 normalize_email 方法。在 normalize_email 方法中,我們將 email 屬性轉換為小寫字母,以確保所有郵件地址都是小寫的。

需要注意的是,註冊回調時必須指定要執行的方法的名稱,可以是一個實例方法或一個類方法。回調方法中可以使用模型對象的任何屬性或方法來執行特定的邏輯,例如更新其他對象、發送電子郵件等。

使用回調可以讓我們更靈活地控制模型對象的行為,可以在對象的生命週期中執行任意的操作。同時,回調也可以提高代碼的可讀性和可維護性,使代碼更易於理解和修改。

Available Callbacks

在 Rails 中,可以註冊多種類型的回調來在模型對象的生命週期中執行特定代碼。以下是可用的回調類型:

創建和保存對象

  • before_validation:在驗證對象之前執行回調。
  • after_validation:在驗證對象之後執行回調。
  • before_save:在保存對象之前執行回調,包括新建和更新操作。
  • around_save:在保存對象之前和之後執行回調,使用 yield 方法來執行保存操作。
  • after_save:在保存對象之後執行回調,包括新建和更新操作。
  • before_create:在創建對象之前執行回調。
  • around_create:在創建對象之前和之後執行回調,使用 yield 方法來執行創建操作。
  • after_create:在創建對象之後執行回調。

更新和刪除對象

  • before_update:在更新對象之前執行回調。
  • around_update:在更新對象之前和之後執行回調,使用 yield 方法來執行更新操作。
  • after_update:在更新對象之後執行回調。
  • before_destroy:在刪除對象之前執行回調。
  • around_destroy:在刪除對象之前和之後執行回調,使用 yield 方法來執行刪除操作。
  • after_destroy:在刪除對象之後執行回調。

關聯對象

  • before_add_association:在添加關聯對象之前執行回調。
  • after_add_association:在添加關聯對象之後執行回調。
  • before_remove_association:在刪除關聯對象之前執行回調。
  • after_remove_association:在刪除關聯對象之後執行回調。

其他

  • after_initialize:在實例化對象之後執行回調。
  • after_find:在從數據庫中查找對象之後執行回調。

要註冊回調,可以在模型類中使用相應的回調方法來指定回調的類型,然後指定要執行的方法。例如,要在保存對象之前執行特定的邏輯,可以使用 before_save 方法:

class User < ApplicationRecord
  before_save :normalize_email

  private

  def normalize_email
    self.email = email.downcase
  end
end

在上面的例子中,我們將 normalize_email 方法註冊為 before_save 回調。這意味着在保存 User 對象之前,Rails 將自動調用 normalize_email 方法。在 normalize_email 方法中,我們將 email 屬性轉換為小寫字母,以確保所有郵件地址都是小寫的。

需要注意的是,回調方法中可以使用模型對象的任何屬性或方法來執行特定的邏輯,例如更新其他對象、發送電子郵件等。使用回調可以讓我們更靈活地控制模型對象的行為,可以在對象的生命週期中執行任意的操作。同時,回調也可以提高代碼的可讀性和可維護性,使代碼更易於理解和修改。

回調前更新屬性會怎麼辦

如果在回調中嘗試更新屬性,可能會導致一些問題。因為回調的執行順序是不確定的,所以在某些情況下,屬性的更新可能會被其他回調覆蓋或被數據庫中的持久化數據覆蓋。

例如,如果我們在 before_save 回調中嘗試更新某個屬性,而在 after_save 回調中有另一個回調也嘗試更新同一個屬性,那麼最終屬性值可能會是不確定的,因為最後執行的回調會覆蓋之前的值。

為了避免這種情況,應該儘量避免在回調中更新屬性。如果確實需要更新屬性,可以使用 update_column 方法來更新屬性,該方法可以直接將屬性更新到數據庫中,而不觸發其他回調。但是需要注意,使用 update_column 方法將跳過所有的驗證,包括模型定義的驗證,因此需要謹慎使用。

另外,如果在回調中需要使用其他模型對象的數據,可以將邏輯移動到控制器或服務對象中,以確保數據正確性和可維護性。

after_initialize and after_find

after_initializeafter_find 都是 ActiveRecord 模型中的回調方法。

after_initialize 方法會在創建新的 ActiveRecord 對象或從數據庫中加載現有對象時被調用。該方法可以用來執行任意初始化邏輯,例如設置默認值或初始化關聯對象。與其他回調不同,after_initialize 方法不需要接收任何參數,因為它是在對象創建之後立即調用的。

以下是一個示例,演示如何使用 after_initialize 方法在創建新對象時設置默認值:

class User < ApplicationRecord
  after_initialize :set_defaults

  private

  def set_defaults
    self.status ||= "active"
  end
end

在上面的例子中,我們將 set_defaults 方法註冊為 after_initialize 回調。在 set_defaults 方法中,我們檢查 status 屬性是否為 nil,如果是,則將其設置為默認值 "active"

after_find 方法會在從數據庫中查找 ActiveRecord 對象之後被調用。該方法可以用來執行任意的後處理邏輯,例如計算屬性或更新關聯對象。after_find 方法接收一個參數,即從數據庫中加載的 ActiveRecord 對象。

以下是一個示例,演示如何使用 after_find 方法計算用户的年齡:

class User < ApplicationRecord
  after_find :calculate_age

  private

  def calculate_age
    self.age = Date.today.year - birthday.year
  end
end

在上面的例子中,我們將 calculate_age 方法註冊為 after_find 回調。在 calculate_age 方法中,我們使用從數據庫中加載的用户對象的生日屬性計算用户的年齡,並將結果保存到年齡屬性中。

需要注意的是,after_find 方法只會在從數據庫中加載對象時被調用,而不會在實例化新對象時被調用。如果需要在對象創建後執行某些邏輯,應該使用 after_initialize 方法。

after_touch什麼 意思

after_touch是Rails中的一個回調方法,它會在一個已關聯的對象被touch操作更新後被觸發。在Rails中,touch操作指的是在更新一個對象時,同時更新關聯對象的更新時間戳(updated_at)字段。這個操作可以用來實現緩存失效、重新計算統計數據等功能。

例如,假設你有一個User模型和一個Post模型,一個用户可以擁有多篇文章。當你更新某篇文章時,你可能需要更新相關用户的更新時間戳,以便在用户列表或其他地方正確地排序。你可以使用touch選項來實現這一點,如下所示:

class Post < ApplicationRecord
  belongs_to :user, touch: true
end

class User < ApplicationRecord
  has_many :posts
  after_touch :update_sorting

  def update_sorting
    # 更新用户排序,例如更新`updated_at`字段
    self.touch
  end
end

這段代碼定義了兩個Active Record模型,PostUser,它們之間存在一個一對多的關聯關係。

Post模型中,使用belongs_to :user, touch: true聲明瞭一個屬於關聯,表示一篇文章屬於一個用户。touch: true選項表示當文章被更新時,自動更新與之關聯的用户的updated_at字段,以便在用户列表或其他地方正確地排序。

User模型中,使用has_many :posts聲明瞭一個擁有多個關聯,表示一個用户可以擁有多篇文章。after_touch :update_sorting聲明瞭一個after_touch回調方法,表示當與之關聯的一篇文章被touch操作更新時,自動調用update_sorting方法更新用户排序,例如更新updated_at字段。

update_sorting方法中,使用self.touch方法更新用户的updated_at字段,以便在用户列表或其他地方正確地排序。

需要注意的是,這段代碼中使用了touch操作來實現自動更新關聯對象的更新時間戳,這可以用來實現緩存失效、重新計算統計數據等功能。在Rails中,touch操作是一種常見的技巧,可以用來簡化代碼並提高性能。

需要注意的是,after_touch回調方法只會在touch操作觸發更新時被調用。如果你手動更新了updated_at字段,after_touch回調方法不會被調用。

運行回調

這些是Rails中常用的Active Record模型操作方法,下面分別介紹它們的作用:

  1. create(attributes = {}):創建一個新的模型對象並將其保存到數據庫中。可以傳入一個哈希表參數attributes表示要創建的模型對象的屬性值。

  2. create!(attributes = {}):與create相同,但如果保存失敗會拋出異常。

  3. destroy:從數據庫中刪除當前模型對象。

  4. destroy!:與destroy相同,但如果刪除失敗會拋出異常。

  5. destroy_all:刪除符合條件的所有模型對象,不進行任何回調和驗證。

  6. destroy_by(conditions):根據條件刪除符合條件的單個模型對象,不進行任何回調和驗證。

  7. save:將當前模型對象的屬性值保存到數據庫中。如果對象不存在,則創建一個新的對象。

  8. save!:與save相同,但如果保存失敗會拋出異常。

  9. save(validate: false):與save相同,但不進行模型對象的驗證。

  10. toggle!:將當前模型對象的布爾類型屬性取反並保存到數據庫中。

  11. touch:更新當前模型對象的updated_at字段,並保存到數據庫中。這個方法通常用於更新緩存或觸發回調方法。

  12. update_attribute(name, value):更新當前模型對象的單個屬性值,並直接保存到數據庫中,不進行任何驗證。

  13. update(attributes):更新當前模型對象的屬性值,並保存到數據庫中。可以傳入一個哈希表參數attributes表示要更新的屬性值。

  14. update!(attributes):與update相同,但如果更新失敗會拋出異常。

  15. valid?:檢查當前模型對象是否通過驗證。如果驗證失敗,可以使用errors方法查看錯誤信息。

這些方法是Rails中常用的Active Record模型操作方法,可以用於創建、更新、刪除和驗證模型對象。需要注意的是,這些方法中有些會觸發回調方法、進行驗證或拋出異常,具體使用時需要根據實際情況選擇合適的方法。

user avatar liutos Avatar jzgw Avatar
Favorites 2 users favorite the story!
Favorites

Add a new Comments

Some HTML is okay.