Active Record 回调
对象的生命周期
回调发生在数据对象生命周期某个事件之前或者之后,例如创建、更新和销毁。
回调简介
注册回调
实例方法通过实例的注册方法注册可变成回调方法,例如before_validation:
class User < ActiveRecord::Base
validates :login, :email, presence: true
before_validation :ensure_login_has_a_value
before_create do
self.name = login.capitalize if name.blank?
end
protected def ensure_login_has_a_value
if login.nil?
self.login = email unless email.blank?
end
end
end
回调方法一般被定义为private或者protected方法,防止外部被调用。
可用的回调
| 分类 | 回调 |
|---|---|
| 创建对象 | before_validationafter_validationbefore_savearound_savebefore_createaround_createafter_createafter_save |
| 更新对象 | before_validationafter_validationbefore_savearound_savebefore_updatearound_updateafter_updateafter_save |
| 销毁对象 | before_destroyaround_destroyafter_destroy |
after_find从数据库中读取记录后执行,初始化之前调用after_initialize在初始化完成之后调用 | |
after_touch??? |
创建对象和跟新对象都会先触发validation和save事件,在save动作中选择创建(create)或者更新(update)。
执行回调
会触发回调执行的方法有:
createandcreate!decrement!andincrement!destroy,destroy!anddestroy_allsave,save!andsave(validate: false)toggle!update,update!andupdate_attributevalid?andinvalid?
会触发after_find的查询方法有:
all,firstandlastfindandfind_byfind_by_*andfind_by_*!find_by_sql
after_initialize在新对象初始化后执行。
跳过回调
部分方法不会触发回调,使用时需要特别注意:
decrementanddecrement_counterincrementandincrement_counterdeleteanddelete_alltoggletouchupdate_columnandupdate_columnsupdate_allupdate_counters
终止执行
基于模型的一系列操作会被放入一个执行队列中依次执行,例如数据验证、回调、更新数据库等。
整个任务队列将在一个事务(transaction)中被完成。在执行队列的过程中,某些异常可能会导致当前事务失败,从而被撤销,比如:
- 任何一个
before_*回调方法返回false或者抛出异常,会导致回调链终止,事务被撤销; - 任何一个
after_*回调方法抛出异常,也会导致终止回调链和撤销事务 - ……
关联回调
例如,如果关联模型之间设置了删除绑定,则在删除对象时会触发关联对象与删除相关的回调。
条件回调
使用:if or :unless选项为回调方法指定回调触发的前提条件。
class Order < ActiveRecord::Base
# 一个方法
before_save :normalize_card_number, if: :paid_with_card?
# 一个 Ruby 语句
before_save :normalize_card_number, if: "paid_with_card?"
# 一个 Proc 语句
before_save :normalize_card_number, if: Proc.new { |order| order.paid_with_card? }
# 多个条件
after_create :send_email_to_author,
if: :author_wants_emails?,
unless: Proc.new { |comment| comment.post.ignore_comments? }
end
回调类
回调类可以重用回调方法。回调方法可以定义为回调类的类方法或者实例方法:
-
类方法的回调方法:使用时无需实例化
class PictureFileCallbacks def self.after_destroy(picture_file) end end class PictureFile < ActiveRecord::Base after_destroy PictureFileCallbacks end -
实例方法的回调方法:使用时需要实例化,可以使用实例的状态
class PictureFileCallbacks def after_destroy(picture_file) end end class PictureFile < ActiveRecord::Base after_destroy PictureFileCallbacks.new end事务回调
事务回调在数据库事务完成之后被触发执行。
after_commit在数据库操作提交后被触发,after_rollback在事务回滚后触发。参考
https://ruby-china.github.io/rails-guides/v4.1/active_record_callbacks.html
评论区