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

时间:2015-01-02 06:26:50

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

我有一个before_save,需要先创建/保存记录。

如果记录不是before_save,那么我是否可以执行new,如果记录不是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" = $1 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" = $1 LIMIT 1  [["id", 46]]
=> 4
[159] pry(main)> p.max_tree_depth
=> nil

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

如果我使用了before_save,它会正确更新属性 - 但仅限于现有记录。在以前没有保存过的全新记录中,ancestry gem会引起人们的注意。

2 个答案:

答案 0 :(得分:1)

以下内容应达到您的目的:

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只是为了确保在保存后保存AND之前不会调用相同的方法。如果在保存之前和之后运行它两次是安全的,那么你可以简化这样的事情:

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回调。

答案 1 :(得分:0)

您所要做的就是重新加载对象以保持持久性

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