Почему проверка присутствия 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 это небрежно:

Обратите внимание, что функция автосохранения будет автоматически добавлена ​​к отношению при использовании accepts_nested_attributes_for или проверке наличия отношения.

Но 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 включает автосохранение? Как это может быть желаемым поведением по умолчанию? Пожалуйста, просветите меня.


person Stefan    schedule 10.06.2016    source источник


Ответы (1)


Похоже, что автоматическое включение автосохранения было добавлено в Mongoid 3.0 намеренно, чтобы его поведение соответствовало ActiveRecord. Взгляните на эти две проблемы:

Вторая проблема связана с документом ActiveRecord, который действительно ведет себя точно так же, особо процитируем следующее утверждение:

Обратите внимание, что autosave: false — это не то же самое, что не объявлять :autosave. Если параметр :autosave отсутствует, новые записи ассоциаций сохраняются, но обновленные записи ассоциаций не сохраняются.

Вот ссылка на исходный код сама монгоидная черта.

Кроме того, из всех этих источников выясняется, что ваше решение было идеальным, и вам действительно следует специально указать :autosave => false, чтобы отключить эту функцию.

person BoraMa    schedule 10.06.2016