Mongoid删除未保存的嵌入式文档

时间:2019-01-16 19:40:34

标签: ruby-on-rails mongoid

我在一个以用户为父文档的应用程序中使用mongoid,几乎所有其他信息都嵌入在用户中。因此,例如,我对属于用户的#new的控制器Relationship动作如下:

def new
  @relationship = current_user.relationships.new(friend_id: params[:fid])
  @relationship.validate
end

因为我对将在视图中显示的关系进行验证,并且其中一些验证需要能够引用父级,所以我不能只调用@relationship = Relationship.new(friend_id: params[:fid]),而是在实例中实例化了该关系。用户的关系数组,即使用户决定不想再建立新的关系,而现在又去了网站的另一部分,它现在仍然在那里闲逛。如果他们转到关系索引页面,除非我将其过滤掉,否则他们会在列表中看到它。

如果该关系有效,并且他们在其他地方进行了一些操作导致用户保存,则该虚拟关系现在是真实的。如果无效,保存将因未知原因而失败。

我打算在用户中嵌入许多模型,因此每个模型都会遇到这个问题。

我知道我可以打电话给current_user.reload来清除垃圾,但是对我来说,每次我想这样做都必须打数据库是很荒谬的。验证后,我也可以孤立该关系,但这感觉很不容易。

在我看来,这是人们始终会遇到的嵌入式文档问题,因此我认为应该有某种内置的解决方案,但似乎找不到任何解决方案。我看到了this question,它与我的相似,但是我想要更可扩展的东西,这样我就不必把它放到任何地方。

我将要创建一个模块,为每个嵌入式关系的类添加一个clear_unsaved_#{relation}方法,但是这个想法使我感到沮丧,所以我想看看是否有人对如何做有更好的想法它,以及最好在哪里调用它。

1 个答案:

答案 0 :(得分:1)

我最终制作了一个覆盖Mongoid的embeds_manyembeds_one类方法的猴子补丁,还定义了一个实例方法来清除该关系的未保存文档。这对我来说是最直接的方法,因为它很少的代码,这意味着我不必记住将其包含在位置。

# config/initializers/patches/dirty_tracking_embedded.rb
module DirtyTrackingEmbedded
    # override the embedding methods to also make dirty-tracking
    def embeds_many(name, options= {}, &block)
        define_method "clear_unsaved_#{name}" do
            # remove_child removes it from the array without hitting the database
            send(name).each {|o| remove_child(o) unless o.persisted?}
        end
        super
    end

    def embeds_one(name, options={}, &block)
        define_method "clear_unsaved_#{name}" do
            dirty = send(name)
            remove_child(dirty) unless dirty.persisted?
        end
        super
    end
end

module Mongoid
    module Association
        module Macros
            module ClassMethods
                prepend DirtyTrackingEmbedded
            end
        end
    end
end

然后在我的控制器中,我求助于after_action

# app/controllers/relationships_controller.rb
class RelationshipsController < ApplicationController
    after_action :clear_unsaved, only: [:new]

    def new
        @relationship = current_user.relationships.new(friend_id: params[:fid])
        @relationship.validate
    end

    private
    def clear_unsaved
        current_user.clear_unsaved_relationships
    end
end

其他可能性

不同的猴子片

您可以猴子修补Mongoid::Association::Embedded::EmbedsManyMongoid::Association::Embedded::EmbedsOne中的setup_instance_methods!方法,以包括设置一个实例方法来清除未保存的方法。通过查看Mongoid::Association::Accessors#self.define_ids_setter!,您可以找到蒙古人如何做这类事情的示例。我建议像我使用的解决方案一样使用prepend进行修补,以便您可以继承该方法的其余部分。

组合猴子补丁和继承

Mongoid选择用于实例化a constant called MACRO_MAPPING in Mongoid::Association的关联的类,因此您可以使从EmbedsManyEmbedsOne继承而来的类仅覆盖setup_instance_methods!来添加所需的实例方法,则只需猴子补丁MACRO_MAPPING即可映射到新类。

关注

如果您要进行反猴子修补,则可以使用我的DirtyTrackingEmbedded模块中的代码来制作一个ActiveSupport::Concern来执行相同的功能。您需要将重写的方法放在class_methods块中,然后确保在将Mongoid::Document包含在任何您要加入的模型类中之后,确保包含此模块。