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_validation after_validation before_save around_save before_create around_create after_create after_save |
更新对象 | before_validation after_validation before_save around_save before_update around_update after_update after_save |
销毁对象 | before_destroy around_destroy after_destroy |
after_find 从数据库中读取记录后执行,初始化之前调用after_initialize 在初始化完成之后调用 | |
after_touch ??? |
创建对象和跟新对象都会先触发validation和save事件,在save动作中选择创建(create)或者更新(update)。
执行回调
会触发回调执行的方法有:
create
andcreate!
decrement!
andincrement!
destroy
,destroy!
anddestroy_all
save
,save!
andsave(validate: false)
toggle!
update
,update!
andupdate_attribute
valid?
andinvalid?
会触发after_find
的查询方法有:
all
,first
andlast
find
andfind_by
find_by_*
andfind_by_*!
find_by_sql
after_initialize
在新对象初始化后执行。
跳过回调
部分方法不会触发回调,使用时需要特别注意:
decrement
anddecrement_counter
increment
andincrement_counter
delete
anddelete_all
toggle
touch
update_column
andupdate_columns
update_all
update_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
评论区