保存和更新子关联对象

时间:2016-01-29 02:34:46

标签: ruby-on-rails rails-activerecord

我有一个类似的模型:

class Content < ActiveRecord::Base
  has_many :revisions
end

class Revision < ActiveRecord::Base
  belongs_to :Content
end

在我的控制器中我正在做:

def create
  @content = Content.new(content_params)

  if @content.save
     # ...
  end
end

内容有:

- id
- category_id
- title
- body
- ..

修订版已经:

- id
- category_id
- content_id
- body

因此,每当我保存/更新/删除内容时,修订版都会发生同样的事情。

我应该使用相同的方法创建一个before_save来处理这两种情况吗? 如何轻松地将category_id,content_id和body传递给Revision?

更新

在更新期间,如果我要更新特定的revision_id,我必须拥有该revision_id但不确定在更新Content实例时如何传递它。这有意义吗?

2 个答案:

答案 0 :(得分:1)

我可以看看ActiveRecord::Callbacks。以下是如何使用它们的示例:

 class Content < ActiveRecord::Base
   has_many :revisions, dependent: :destroy

   after_commit :update_revision

   private

   def update_revision
     # Create or update a revision object
   end
 end

一般来说,我不喜欢使用回调创建另一个模型的想法。这感觉违反了单一责任原则。您可以使用服务对象来处理您提到的创建和更新操作。

请注意dependent: destroy关系中的has_many语法,如上面的代码块所示。这会将Content记录的删除级联到其相关的Revision

<强>更新

如果您只想更新控制器内的特定版本,可以写:

 def create
   @content = Content.new(content_params)

   if @content.save
     Revision.find(revision_id).update_attributes!(...)
   end
 end

答案 1 :(得分:0)

您问题的直接答案是使用before_update回调,inverse_of(因此相关对象在内存中可用):

#app/models/content.rb
class Content < ActiveRecord::Base
  has_many :revisions
  accepts_nested_attributes_for :revisions

  before_update :set_revision
  before_create :set_revision

  private

  def set_revision
     self.revisions.build body: self.body
  end
end

#app/models/revision.rb
class Revision < ActiveRecord::Base
  belongs_to :content
end

每次revision update模型时,上述内容都会创建一个新的content。两者都会在您使用accepts_nested_attributes_for

时保存

作为推荐,可能希望查看Paper Trail gem。

完全您正在寻找的内容(就修订而言) - 它会保存您在模型上执行的所有更新/修改。

#Gemfile
gem 'paper_trail'

$ rails generate paper_trail:install
$ rake db:migrate

#app/models/content.rb
class Content < ActiveRecord::Base
  has_paper_trail
end

这将自动记录对执行它的模型的更改。

good RailsCast about it here