Mongoid embeds_many:不保存就推送文档,以保留脏状态

flo*_*ish 5 mongoid

在Mongoid中,将文档推入embeds_many关系会自动将文档持久保存到数据库中。通常,这很好,但是当我需要跟踪对嵌入式文档的更改时遇到了问题。

假设您有两种模式:

class List
  include Mongoid::Document
  embeds_many :items
  field :title
end

class Item
  include Mongoid::Document
  embedded_in :list
  field :name
end
Run Code Online (Sandbox Code Playgroud)

这发生在.changes属性上:

list = List.new(title: 'List title')
list.save  #list is now persisted
item = Item.new(name: 'Item name')
item.changes  #returns Hash with {'name' => [nil, 'Item name']}
list.items << item  #saves item to database under the hood
item.changes  #returns empty Hash, because item was autosaved with list
Run Code Online (Sandbox Code Playgroud)

我可以item.previous_changes用来检查在将项目推入列表之前所做的更改,但是在我的特定情况下,这会给我带来各种麻烦,使事情难以管理。

我想要实现的是能够初始化Item文档,然后将其添加到list(通过<<push),而无需立即将其持久化。

我知道Mongoid确实提供了一种建立embeds_many关系而不持久的选项(请参阅http://mongoid.org/en/mongoid/docs/relations.html#embeds_many):

list.items.build(name: 'Another item')
Run Code Online (Sandbox Code Playgroud)

问题在于Mongoid为您创建了Item实例。就我而言,该embeds_many关系中的文档可能是Item的子类(例如SpecialItem < Item),无法与配合使用build。但是,如果有人知道解决此限制的方法,我也很乐意接受它作为答案。

flo*_*ish 5

回答我自己的问题:通过将父级文档分配给子级而不是将子级添加到列表中来解决此问题。

继续上面的示例,您应该执行

item.list = list  #no database query
Run Code Online (Sandbox Code Playgroud)

代替

list.items << item  #automatic database insert
Run Code Online (Sandbox Code Playgroud)

设置父子引用,而无需将任何内容自动保存到数据库。


Tap*_*nen 2

要使用您的示例跟进“构建子类”问题,您可以:

list.items.build({
  name: "Another Item"
}, SpecialItem)
Run Code Online (Sandbox Code Playgroud)

指定您希望 Mongoid 为您构建的(子)类。