为什么 Mongoid 的 "presence" 验证会启用自动保存?

Why does Mongoid's "presence" validation enable autosave?

我很难调试这个,我正要寻求帮助。但我已经设法找出原因,并想分享我的发现,以防其他人 运行 遇到同样的问题。 [也许有人可以解释 为什么 它以这种方式工作]

设置

假设我有两个 Mongoid 文档,CustomerOrder 具有 1:n 关系。此外,Customer 有一个 after_save 回调来与外部同步文档更改 API:

class Customer
  include Mongoid::Document
  has_many :orders
  after_save do
    puts "synchronizing customer" # <- not my actual code
  end
end

class Order
  include Mongoid::Document
  belongs_to :customer
  validates_presence_of :customer
end

一切正常。创建和更新客户会导致 after_save 回调被触发,而创建订单则不会。

改变

过了一会儿,Customer 需要一个具有默认值的新字段:

class Customer
  # ...
  field :premium, type: Boolean, default: false
end

问题

但是突然之间,事情变得很奇怪。在此更改之后,创建(或更新)订单也会导致客户被保存! (我注意到这一点是因为我的日志——同步是 运行 没有明显的原因)

c = Customer.last
c.orders.create
synchronizing customer  # <- what the?
#=> #<Order _id: 575a995aab265d730b8bddba ...>

奇怪的是,这只发生在现有客户身上,而且只发生一次。

原因

冗长乏味的调试会话显示 Orderbelongs_to 关系有一个 autosave 标志:

Order.relations['customer'].autosave?
#=> true

它是通过存在验证启用的,事实上,Mongoid 的 documentation 随便注意到它:

Note that autosave functionality will automatically be added to a relation when using accepts_nested_attributes_for or validating presence of the relation.

但是autosave只在文档发生变化时才保存文档,那么变化是从哪里来的呢?显然,我的具有默认值的新 premium 字段引入了一个细微的变化:

c = Customer.first # a customer from before the change without "premium" attribute
c.changed?
#=> true
c.changes
#=> {"premium"=>[nil, false]}

解决方案

毕竟,修复是微不足道的。我只需要在我的 belongs_to 关系上明确禁用自动保存:

class Order
  include Mongoid::Document
  belongs_to :customer, autosave: false
  validates_presence_of :customer
end

未决问题

但问题仍然存在:为什么 Mongoid 的 "presence" 验证会启用自动保存?这怎么可能是期望的默认行为?请赐教。

似乎有意将这种自动启用自动保存添加到 Mongoid 3.0 ,以便其行为与 ActiveRecord 一致。看看这两个问题:

第二个问题链接到一个 ActiveRecord doc 似乎确实表现相同,让我们特别引用以下声明:

Note that autosave: false is not same as not declaring :autosave. When the :autosave option is not present then new association records are saved but the updated association records are not saved.

这是 Mongoid 功能本身的 link to the source code

此外,从所有这些来源来看,您的解决方案是完美的,您确实应该明确说明:autosave => false禁用此功能