侧边栏壁纸
  • 累计撰写 218 篇文章
  • 累计创建 59 个标签
  • 累计收到 5 条评论

Rails 学习笔记 04: Active Record 回调

barwe
2021-09-15 / 0 评论 / 0 点赞 / 432 阅读 / 2,415 字
温馨提示:
本文最后更新于 2022-07-18,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

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 and create!
  • decrement! and increment!
  • destroy, destroy! and destroy_all
  • save, save! and save(validate: false)
  • toggle!
  • update, update! and update_attribute
  • valid? and invalid?

会触发after_find的查询方法有:

  • all, first and last
  • find and find_by
  • find_by_* and find_by_*!
  • find_by_sql

after_initialize在新对象初始化后执行。

跳过回调

部分方法不会触发回调,使用时需要特别注意:

  • decrement and decrement_counter
  • increment and increment_counter
  • delete and delete_all
  • toggle
  • touch
  • update_column and update_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

0

评论区