【问题标题】:Mongoid remove unsaved embedded documentsMongoid 删除未保存的嵌入文档
【发布时间】:2019-01-16 19:40:33
【问题描述】:

我将 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} 方法到类中,但这个想法让我感到沮丧,所以我想看看是否有人对如何做有更好的想法,以及最好在哪里调用它。

【问题讨论】:

    标签: ruby-on-rails mongoid


    【解决方案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! 找到 Mongoid 人如何做这类事情的示例。我建议您使用 prepend 进行修补,就像我使用的解决方案一样,这样您就可以继承该方法的其余部分。

    组合猴子补丁和继承

    Mongoid 选择使用哪个类来实例化 a constant called MACRO_MAPPING in Mongoid::Association 的关联,因此您可以创建从 EmbedsManyEmbedsOne 继承的类,只需覆盖 setup_instance_methods! 以添加所需的实例方法,那么您只需必须修改 MACRO_MAPPING 才能映射到您的新类。

    关注

    如果你是反猴子补丁,你可以使用我的 DirtyTrackingEmbedded 模块中的代码来创建一个 ActiveSupport::Concern 来做同样的事情。您需要将覆盖的方法放在 class_methods 块中,然后确保在将 Mongoid::Document 包含在您想要的任何模型类中之后包含此模块。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-04-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多