Docs 菜单

为数据模型自定义回调

在本指南中,您可以学习;了解如何在 Mongoid 模型中实现回调,以自定义模型实例的生命周期。

回调是 Mongoid 在对象生命周期的指定时刻触发的方法。 它们允许您在对象状态更改之前或之后启动指定的操作。

Mongoid 实现了来自 Active Record 的许多回调。 要学习;了解详情,请参阅 Active Record 文档中的 回调 。

Mongoid 支持对实现 文档模块的模型类进行以下回调:

  • after_initialize

  • after_build

  • before_validation

  • after_validation

  • before_create

  • around_create

  • after_create

  • after_find

  • before_update

  • around_update

  • after_update

  • before_upsert

  • around_upsert

  • after_upsert

  • before_save

  • around_save

  • after_save

  • before_destroy

  • around_destroy

  • after_destroy

要学习;了解有关上述任何回调类型的更多信息,请参阅 Rails API文档中的 ActiveRecord::Callbacks 参考。

您可以在顶层和嵌入式文档模型中实现回调。

注意

回调调用行为

为了效率,Mongoid 仅对您执行持久性动作的文档回调回调。 这种行为使 Mongoid 能够支持大型层次结构,并通过不在整个文档层次结构中调用回调来有效地处理优化的原子更新。

在实现领域逻辑的回调时,请采取预防措施并确保可测试性,因为当链中的回调停止执行时,这些设计可能会导致意外错误。 我们建议对程序核心功能之外的横切关注点使用回调,例如对背景作业进行排队。

您必须在模型类上实现和注册回调。 您可以使用普通方法、区块和 Proc 对象来注册回调,也可以定义使用类或模块的自定义回调对象。

此示例演示了如何通过以下方式在 Contact 模型类上注册回调:

  • 包括 before_save 类方法,该方法会在将 Contact实例保存到MongoDB之前触发 process_phone 方法。 process_phone 方法在类中单独定义。

  • 包括 after_destroy 类方法,并在删除 Contact实例时使用区块打印消息。

class Contact
include Mongoid::Document
field :name, type: String
field :phone, type: String
# Creates a callback to clean phone numbers before saving
before_save :process_phone
protected
def process_phone
self.phone = phone.gsub(/[^0-9]/, "") if attribute_present?("phone")
end
# Creates a callback to send a message about object deletion
after_destroy do
p "deleted the contact for #{name}"
end
end

以下代码执行演示回调操作的数据操作:

Contact.create(name: 'Serena Atherton', phone: '999 555-3030')
# => `phone` field saved as '9995553030'
Contact.create(name: 'Zayba Haq', phone: '999 123?5050')
# => `phone` field saved as '9991235050'
Contact.first.destroy
# => Console message: "deleted the contact for Serena Atherton"

由于回调功能来自 Active Support,因此您也可以使用 set_callback 类方法语法来注册回调。 以下代码演示了如何使用此语法创建回调,将 name字段的原始值存储在 aliases大量中:

class Contact
include Mongoid::Document
field :name, type: String
field :phone, type: String
field :aliases, type: Array, default: []
set_callback(:update, :before) do |document|
if document.name_changed?
document.push(aliases: document.name_was)
end
end
end
Contact.create(name: 'Xavier Bloom', phone: '4447779999')
Contact.first.update(name: 'Xav - coworker')
# Saved document in MongoDB:
# {"aliases":["Xavier Bloom"],"name":"Xav - coworker","phone":"4447779999"}

Mongoid 提供以下关联回调:

  • after_add

  • after_remove

  • before_add

  • before_remove

如果您在模型类上注册关联回调,则每当您从以下任何关联中添加或删除文档时都会调用该关联回调:

  • embeds_many

  • has_many

  • has_and_belongs_to_many

将关联回调指定为相应关联的选项。 您必须将添加或删除的文档作为参数传递给指定的回调。

以下代码演示了如何在嵌入多个 SavedArticle 实例的 User 模型类上注册关联回调,以限制单个实例嵌入文档的数量:

class User
include Mongoid::Document
field :username, type: String
# Registers the callback in the association statement
embeds_many :saved_articles, before_add: :send_message
protected
# Passes the association document as a parameter to the callback
def send_message(saved_article)
if saved_articles.count >= 10
p "you can't save more than 10 articles at a time"
throw(:abort)
end
end
end
class SavedArticle
include Mongoid::Document
embedded_in :user
field :url, type: String
end

要学习;了解如何阻止 Mongoid运行回调,请参阅 Active Record 文档中的以下参考资料:

要学习;了解Mongoid 如何管理事务中的回调,请参阅 事务和会话指南。

要学习;了解如何访问权限和更改MongoDB数据,请参阅“与数据交互”指南。