ruby-on-rails - 为什么 Mongoid 的 "presence"验证启用自动保存?

标签 ruby-on-rails validation mongoid autosave

我在调试这个问题时遇到了困难,我正想寻求帮助。但我已经设法找出原因,并希望分享我的发现,以防其他人遇到同样的问题。 [也许有人可以解释为什么它会这样工作]

设置

假设我有两个 Mongoid-Documents,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 回调被触发,而创建订单则不会。

更改

过了一段时间,客户需要一个具有默认值的新字段:

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

问题

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

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

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

原因

漫长而乏味的调试 session 显示,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 的“存在​​”验证会启用自动保存?这怎么可能是期望的默认行为?请赐教。

最佳答案

看来,这种自动保存功能已被有意添加到 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.

这是一个link to the source code Mongoid 功能本身。

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

关于ruby-on-rails - 为什么 Mongoid 的 "presence"验证启用自动保存?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37746804/

相关文章:

javascript - 如何确保一个对象数组只包含一个带有 Joi 的特定键?

javascript - 防止基于特定字段的 HTML5 验证

MongoDB 映射/减少 "NoMethodError: undefined method ` map_reduce' for #<Moped::Collection"

ruby-on-rails - Mongoid 创建空集合

ruby-on-rails - mina 部署未定义方法 set_default

ruby-on-rails - 如何在ruby中实时解析ffmpeg进度

ruby-on-rails - ActionCable - 无法升级到 WebSocket

php - 在 Laravel 中自定义模型存储的验证规则

ruby-on-rails - 在 mongoid 中与组求和

ruby-on-rails - 尝试安装 rubber gem,Nokogiri 把它搞砸了?