更新关联而不保存

时间:2014-11-27 19:51:01

标签: ruby-on-rails ruby-on-rails-4

我有一个模特:

class A < ActiveRecord::Base
  has_many :B
end

我想重置或更新A的B关联,但只能稍后保存:

a = A.find(...)
# a.bs == [B<...>, B<...>]
a.bs = [] 
#or
a.bs = [B.new, B.new]
# do some validation stuff on `a` and `a.bs`

因此可能会出现一些情况,我稍后会调用a.save,也可能不会。在我不打电话a.save的情况下,我希望a.bs保持其原始值,但是一旦我调用a.bs = [],旧的关联就会被销毁,现在{ {1}}。是否有任何简单的方法来设置记录关联而不立即将其保存在数据库中?我查看了Rails的来源,并没有找到任何可以帮助我的东西。

谢谢!

编辑:

我应该补充一点,这是针对现有应用程序的,并且存在一些架构约束,它们不允许我们使用常规的ActiveRecord更新和验证工具。它的工作方式我们有一组A.find(...).bs == []类,它接受参数并为结帐对象分配Updater的值。然后有一组params类,用于验证每个给定参数的结帐对象。 Fianlly,如果一切都很好,我们保存模型。

在这种情况下,我希望在更新程序中更新关联,在验证程序中验证它们,最后,如果一切都结束,则保留它。

总之,这看起来像:

Validater

由于def update apply_updaters(object, params) # do some stuff with the updated object if(validate(object)) object.save(validate: false) end appy_updaters之间存在大量内容,因此交易不是真正的选择。这就是为什么我真的希望不立即坚持更新关联,就像我们对任何其他属性一样。

到目前为止,我所接近的最接近的解决方案是重写关联缓存(object.save)。这看起来像:

target

到了保存的时候,我们需要保持缓存:

# In the updater
A.bs.target.clear
params[:bs].each{|b| A.bs.build(b)}
# A.bs now contains the parameters object without doing any update in the database

这项工作,但这种感觉很糟糕,很容易破坏或产生一些不良的副作用。我想到的另一种方法是添加一个缓存已分配对象的方法new_object = A.bs.target A.bs(true).replace(new_object) 和返回缓存对象的A#new_bs=(如果可用)。

2 个答案:

答案 0 :(得分:2)

好问题。 我可以建议使用属性赋值而不是集合操作。所有验证都将定期执行 - 在save或其他“持续”之后执行。方法。您可以编写自己的方法(在模型中或在单独的验证器中),这将验证集合。

您可以通过属性删除元素并将其添加到集合中 - 删除操作可以通过其他属性_destroy执行,该属性可能是真实的&#39;或者&#39; false&#39; (http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html),另外 - 通过设置父模型来接受属性。

作为示例设置模型A:

class A < ActiveRecord::Base
   has_many :b
   accepts_nested_attributes_for :b, :allow_destroy => true
   validates_associated :b # to validate each element

   validate :b_is_correct # to validate whole collection
   def b_is_correct
      self.bs.each { |b| ... } # validate collection
   end
end

在控制器中使用普通属性进行模型更新(例如update!(a_aparams))。这些方法的行为类似于平面属性更新。并且不要忘记允许嵌套集合的属性。

class AController < ApplicationController
  def update
    @a = A.find(...)
    @a.update(a_attributes) # triggers validation, if error occurs - no changes will be persisted and a.errors will be populated
  end

  def a_attributes
    params.require(:a).permit([:attr_of_a, :b_attributes => [:attr_of_b, :_destroy]])
  end
end

在表单上我们使用了gem nested_form(https://github.com/ryanb/nested_form),我推荐它。但在服务器端,此方法使用前面提到的属性_destroy

答案 1 :(得分:0)

我终于发现了mark_for_destruction方法。我的最终解决方案如下:

a.bs.each(&:mark_for_destruction)
params[:bs].each{|b| a.bs.build(b)}

然后我可以在以下处理和验证中过滤掉marked_for_destruction?条目。

感谢@AlkH,让我了解accepts_nested_attributes_for如何工作并处理延迟破坏关联。