after_destroy回调错误"cannot update on new record object"

after_destroy callback error "cannot update on new record object"

我有一个像这样的 after_destroy 回调:

after_destroy :update_max_depth_on_entire_tree 

定义为:

  def update_max_depth_on_entire_tree
    root = self.root
    root.check_or_update_max_tree_depth
    root.descendants.each { |c| 
      c.check_or_update_max_tree_depth
    }
  end

  def check_or_update_max_tree_depth
    update_columns(max_tree_depth: last_depth)      
  end

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

每当我尝试删除对象时,我都会收到此错误:

ActiveRecord::ActiveRecordError at /posts/detecting-ramsay-is-offered-ceo-job
cannot update on a new record object

这是服务器日志的样子:

Started DELETE "/posts/detecting-ramsay-is-offered-ceo-job" for 127.0.0.1 at 2015-01-05 09:56:24 -0500
Processing by PostsController#destroy as HTML
  Parameters: {"authenticity_token"=>"2xRY+A=", "id"=>"detecting-ramsay-is-offered-ceo-job"}
  User Load (0.5ms)  SELECT  "users".* FROM "users"  WHERE "users"."id" = 3  ORDER BY "users"."id" ASC LIMIT 1
   (2.9ms)  SELECT COUNT(*) FROM "roles" INNER JOIN "users_roles" ON "roles"."id" = "users_roles"."role_id" WHERE "users_roles"."user_id" =  AND (((roles.name = 'admin') AND (roles.resource_type IS NULL) AND (roles.resource_id IS NULL)))  [["user_id", 3]]
   (2.4ms)  SELECT COUNT(*) FROM "roles" INNER JOIN "users_roles" ON "roles"."id" = "users_roles"."role_id" WHERE "users_roles"."user_id" =  AND (((roles.name = 'editor') AND (roles.resource_type IS NULL) AND (roles.resource_id IS NULL)))  [["user_id", 3]]
  Post Load (0.9ms)  SELECT  "posts".* FROM "posts"  WHERE "posts"."slug" = 'detecting-ramsay-is-offered-ceo-job'  ORDER BY "posts"."id" ASC LIMIT 1
   (0.2ms)  BEGIN
  Post Load (5.2ms)  SELECT "posts".* FROM "posts"  WHERE (("posts"."ancestry" ILIKE '69/%' OR "posts"."ancestry" = '69'))
  Role Load (0.6ms)  SELECT "roles".* FROM "roles"  WHERE "roles"."resource_id" =  AND "roles"."resource_type" =   [["resource_id", 69], ["resource_type", "Post"]]
  FriendlyId::Slug Load (2.9ms)  SELECT "friendly_id_slugs".* FROM "friendly_id_slugs"  WHERE "friendly_id_slugs"."sluggable_id" =  AND "friendly_id_slugs"."sluggable_type" =   ORDER BY "friendly_id_slugs".id DESC  [["sluggable_id", 69], ["sluggable_type", "Post"]]
  SQL (1.5ms)  DELETE FROM "friendly_id_slugs" WHERE "friendly_id_slugs"."id" =   [["id", 47]]
  SQL (1.2ms)  DELETE FROM "posts" WHERE "posts"."id" =   [["id", 69]]
  Post Load (0.4ms)  SELECT "posts"."id" FROM "posts"  WHERE "posts"."ancestry" = '69'
   (0.2ms)  ROLLBACK
Completed 500 Internal Server Error in 281ms

ActiveRecord::ActiveRecordError - cannot update on a new record object:
  activerecord (4.1.6) lib/active_record/persistence.rb:272:in `update_columns'
   () /app/models/post.rb:122:in `check_or_update_max_tree_depth'
   () /app/models/post.rb:130:in `update_max_depth_on_entire_tree'

如何调用此方法或实现我想要实现的目标(即在删除 1 个节点时更新树上所有节点的属性)而不出现此错误?

编辑 1

我的路线是这样的:

mark_as_published_post_path PUT /posts/:id/mark_as_published(.:format)  posts#mark_as_published
mark_as_unpublished_post_path   PUT /posts/:id/mark_as_unpublished(.:format)    posts#mark_as_unpublished
posts_path  GET /posts(.:format)    posts#index
POST    /posts(.:format)    posts#create
new_post_path   GET /posts/new(.:format)    posts#new
edit_post_path  GET /posts/:id/edit(.:format)   posts#edit
post_path   GET /posts/:id(.:format)    posts#show
PATCH   /posts/:id(.:format)    posts#update
PUT /posts/:id(.:format)    posts#update
DELETE  /posts/:id(.:format)    posts#destroy
status_path GET /:status(.:format)  posts#status {:status=>/confirmed|unconfirmed|corroborated/}
GET /posts/:id(.:format)    redirect(301, /%{id})
GET /:friendly_id(.:format) posts#show
GET /posts/:friendly_id(.:format)   posts#show
GET /rbt/:name(.:format)    redirect(301)
GET /:name(.:format)    posts#show
root_path   GET /   posts#index

这就是我的 PostController#Destroy 的样子:

  def destroy
    @post = Post.find(params[:id])
    @post.destroy
    respond_to do |format|
      format.html { redirect_to posts_url, notice: 'Report was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

编辑 2

我认为问题在于这部分代码:

  def check_or_update_max_tree_depth
    update_columns(max_tree_depth: last_depth)      
  end

错误似乎是说它在链中的某处遇到了新记录,并且无法在作为新记录的对象上执行 update_columns(max_tree_depth: last_depth)

我该如何解决这个问题?

这个问题已经变得有点难以掌握,但我会试一试。 . .

您的最新更新表明您的问题在于 update_columns 对新记录不起作用。为什么不使用 update_attributes` 呢?

变化:

def check_or_update_max_tree_depth
  update_columns(max_tree_depth: last_depth)      
end

至:

def check_or_update_max_tree_depth
  update_attributes(max_tree_depth: last_depth)      
end

我发现了一个与 update_columns 2012 年以来无法处理新记录的问题相关的讨论,其中可能包含一些信息,可以帮助您重组给您带来麻烦的应用程序部分:https://github.com/rails/rails/issues/7226

更新:

您已经回答了这个问题,但只是为了完整主义者的缘故在这里注明。 . .

另一种解决方案是简单地向您的 update_columns 方法添加一个条件,以检查对象是否为 new_record?

if new_record?
  return
else
  update_column(:max_tree_depth, last_depth)
end

我最终通过做一些事情弄明白了。像这样将我的 update_column(max_tree_depth: last_depth) 包裹在 new_record? 支票中:

  def check_or_update_max_tree_depth
    if new_record?
      return
    else
      update_column(:max_tree_depth, last_depth)
    end
  end

解决了那个错误。

我遇到的另一个错误是,每当我的回调遇到作为根记录的记录时,因为我在祖先中使用 :adopt 孤儿策略来确定如何处理已删除的记录帖子,它正在从 post.children 创建一个新记录并将它们分配给 root 并将所有内容都扔进 wack。

所以我只是像这样修改了我的代码:

  def update_max_depth_on_entire_tree
    if max_tree_depth <= 0
      return
    else
      root = self.root
      root.check_or_update_max_tree_depth
      root.descendants.each { |c|
        c.check_or_update_max_tree_depth
      }
    end
  end

这似乎已经做到了。