如果记录是全新的,我如何执行 'after_save',如果不是,我如何执行 `before_save`?

How do I execute an 'after_save' if a record is brand new, but a `before_save` if it isn't?

我有一个before_save要求记录在created/saved之前。

如果记录不是 new,我是否可以执行 before_save,如果是,则执行 after_save

编辑 1

这是我的方法和回调:

after_save :check_or_update_max_tree_depth

  def check_or_update_max_tree_depth
    self.max_tree_depth = self.last_depth
  end

  def last_depth
    if child_ids.empty?
      return root.max_tree_depth
    else
      return children.map{|c| c.last_depth}.max
    end
  end

新记录创建后,不保存max_tree_depth属性。这是刚刚创建的新记录的示例:

[155] pry(main)> p = Post.last
  Post Load (1.9ms)  SELECT  "posts".* FROM "posts"   ORDER BY "posts"."id" DESC LIMIT 1
=> #<Post id: 63, title: "JPS counter sues", photo: nil, body: "JPS has officially filed a countersuit on crashees...", created_at: "2015-01-05 05:30:39", updated_at: "2015-01-05 05:30:39", user_id: 3, ancestry: "46/54/59", file: nil, status: 1, slug: "jps-counter-sues", publication_status: 1, has_eyewitness: false, youtube_embed_code: "", soundcloud_embed_code: "", ancestry_depth: 3, max_tree_depth: nil>
[156] pry(main)> p.last_depth
  Post Load (0.5ms)  SELECT "posts"."id" FROM "posts"  WHERE "posts"."ancestry" = '46/54/59/63'
  Post Load (1.0ms)  SELECT  "posts".* FROM "posts"  WHERE "posts"."id" =  LIMIT 1  [["id", 46]]
=> 4
[157] pry(main)> p.child_ids
  Post Load (1.5ms)  SELECT "posts"."id" FROM "posts"  WHERE "posts"."ancestry" = '46/54/59/63'
=> []
[158] pry(main)> p.root.max_tree_depth
  Post Load (1.5ms)  SELECT  "posts".* FROM "posts"  WHERE "posts"."id" =  LIMIT 1  [["id", 46]]
=> 4
[159] pry(main)> p.max_tree_depth
=> nil

请注意,其他一切似乎都返回了正确的值。只是每当创建一条新记录时,它不会保存它。

如果我使用 before_save,它会正确更新属性 - 但仅限于现有记录。在以前未保存的全新记录上,ancestry gem 会引起不适。

以下应该可以完成您想要的:

before_save :method_name
after_save :method_name

private

def method_name
  if new_record? || @already_ran
    @already_ran = nil # using 'remove_instance_variable' would be better
    return 
  end

  # your method's original code

  @already_ran = true # pick a better variable name
end

@already_ran 只是为了确保在保存之前和之后不会调用相同的方法。如果在保存之前和之后两次 运行 它是安全的,那么你可以像这样简化事情:

before_save :method_name
after_save :method_name

private

def method_name
  return if new_record?
  # your method's original code
end

或者,根据您的使用情况,您可以只保留 after_save 并完全删除 before_save。但我假设您已经考虑过其他简化选项,例如有一个 after_saveafter_create 回调。

您所要做的就是重新加载持久化对象

after_save :check_or_update_max_tree_depth

def check_or_update_max_tree_depth
  self.reload
  self.max_tree_depth = self.last_depth
  self.save
end

def last_depth
  if child_ids.empty?
    return root.max_tree_depth
  else
    return children.map{|c| c.last_depth}.max
  end
end