【问题标题】:Serialization of Rails ActiveRecord object including associationsRails ActiveRecord 对象的序列化,包括关联
【发布时间】:2011-12-10 23:45:13
【问题描述】:

我为一个包含许多关联的复杂模型提交了一个表单。我需要保存该对象的 2 个副本。一个不会被触及,仅用作最初提交内容的参考,另一个将由我的用户编辑。

我最初对对象和所有关联进行了深度克隆,然后将两者都保存到数据库中,标记了一个可以编辑的原始字段。然而这变得太复杂了,我想要一个更简单的解决方案

我认为我最好的选择是序列化整个对象并将其存储在可编辑对象内的单个字段中。所以我的问题是:是否可以序列化一个对象,包括关联,并将其存储在单个字段中?

我应该在模型级别执行此操作吗?还是应该将表单返回的内容保存在一个字段中? (即 application.original_form = params[:application])。

感谢您的帮助! 瑞恩

编辑---

我正在尝试这种方法:

https://gist.github.com/1298567

我将返回的参数保存在序列化字段中。

有没有人认为这有什么不妥?这似乎是最简单的方法。

再次感谢!

瑞恩

【问题讨论】:

    标签: ruby-on-rails ruby serialization


    【解决方案1】:

    我之前做过类似的事情,但只有一层深层关联。例如。具有一个或多个地址、联系人等的帐户。

    您应该确保您与其他相关模型的关联相当简单,因此没有循环或反向指针

    最好以一种便于以后与模型的当前版本进行比较的方式保存它。您需要从比较中排除“原始”字段;-)

    在第一次保存模型期间,我会对模型及其关联进行深度克隆,并将其保存到主模型中的序列化字段“原始”中。

    我想我会使用 ActiveRecord 的serialize 功能,例如:

    Class YourMainModel
      ...
      serialize :original   # will serialize this ; make this TEXT field in DB
      ...
    end
    

    您要确保数据库中字段的数据类型是文本或字符串,而不是二进制字段! (这会在以后引起错误!)

    然后在模型的 after_save 期间,我会添加一些代码来执行以下操作:

    # do this in the after_save - so the validations have run:
    main_model_object.original ||= main_model_object.deep_clone  # ||= to do this only once
    main_model_object.save(:validate => false) if main_model_object.original_changed?  # save if we added the "copy"
    

    可能还有其他方法可以将其挂钩,但 after_save 具有运行验证的优势。

    您需要确保在第一次保存时创建了所有关联记录,可能您可能想要使用nested_forms Gem 做一个“怪物形式”。

    见:

    https://github.com/moiristo/deep_cloneable(Rails 3 的分叉)

    https://github.com/openminds/deep_cloning(原项目)

    查看其他 Ryan 的 RailsCasts:

    http://railscasts.com/episodes/196-nested-model-form-part-1

    http://railscasts.com/episodes/197-nested-model-form-part-2

    希望对你有帮助


    编辑:

    再想一想,为了限制数据库/数据库转储的大小、简化架构并安全保存原件,您可能希望将原件存储在单独的文档存储中,例如你可以使用 MongoDB。

    如果您不需要经常访问原始文件,那么将其作为结构化文档存储在 MongoDB 中可能会非常有益,并且可以减少您的主应用程序的复杂性。

    当你比较记录时,你会像第一次一样对修改后的模型进行 deep_clone,然后通过原始记录的 'id' 字段查找 MongoDB 记录,然后比较两者(两者都是深度克隆)。

    此解决方案的附加好处:意外修改原始文件更难,因为它没有直接附加到 SQL 数据库记录。例如你不能不小心做object.original = something

    【讨论】:

      【解决方案2】:

      我还没有尝试过,但我敢打赌它听起来非常令人毛骨悚然! 您应该使用实例的冒号并像这样引用它们:

      belongs_to :original_version, :class_name => 'ModelName', :foreign_key => 'original_version_id'
      has_one :original_version, :class_name => 'ModelName', :foreign_key => 'original_version_id'
      belongs_to :user_version, :class_name => 'ModelName', :foreign_key => 'user_version_id'
      has_one :user_version, :class_name => 'ModelName', :foreign_key => 'user_version_id'
      

      是的,您将所有这些都放在同一个模型中,然后编写一些方法来检查实例是这些版本中的哪一个。

      【讨论】:

      • 为什么要复制所有关系?
      【解决方案3】:

      不久前我不得不做类似的事情,并且在这些问题上来回多次。我想我最终确实存储了可编辑的对象图序列化,但作为 XML。在我的情况下,除了针对原始“模板”对象之外,用户不会真正进行编辑,因此没有真正的理由重新实例化对象图。

      但我对其他人的解决方案很感兴趣。

      是否可以发布要点?我也许可以提供更详细的反馈。

      【讨论】:

        【解决方案4】:

        我采用了这种方法:

         def create
            @app = App.new(params[:app])
            @app.original = params[:app]
            respond_to do |format|
              if @app.save
                format.html { redirect_to(done_path(@app.member.id)) }
              else
                format.html { render :action => "new" }
              end
            end
        end
        
        #show the original Application
        def show
           @app = App.new(App.find(params[:id]).original)
        end
        
        #Model
        
        class App < ActiveRecord::Base
          has_one :applicant
        
          serialize :original, Hash
        
          accepts_nested_attributes_for :applicant
        end
        

        对我来说,这似乎是解决问题的最简单方法。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-12-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多