为什么 Mongoid 的 "presence" 验证会启用自动保存?
Why does Mongoid's "presence" validation enable autosave?
我很难调试这个,我正要寻求帮助。但我已经设法找出原因,并想分享我的发现,以防其他人 运行 遇到同样的问题。 [也许有人可以解释 为什么 它以这种方式工作]
设置
假设我有两个 Mongoid 文档,Customer
和 Order
具有 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 ...>
奇怪的是,这只发生在现有客户身上,而且只发生一次。
原因
冗长乏味的调试会话显示 Order
的 belongs_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 一致。看看这两个问题:
- validates_presence_of association should enable autosave
- Behavior of :autosave not consistent with 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
禁用此功能。
我很难调试这个,我正要寻求帮助。但我已经设法找出原因,并想分享我的发现,以防其他人 运行 遇到同样的问题。 [也许有人可以解释 为什么 它以这种方式工作]
设置
假设我有两个 Mongoid 文档,Customer
和 Order
具有 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 ...>
奇怪的是,这只发生在现有客户身上,而且只发生一次。
原因
冗长乏味的调试会话显示 Order
的 belongs_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 一致。看看这两个问题:
- validates_presence_of association should enable autosave
- Behavior of :autosave not consistent with 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
禁用此功能。