after_destroy回调错误“无法更新新记录对象”

时间:2015-01-06 07:39:04

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

我有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" = $1 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" = $1 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" = $1 AND "roles"."resource_type" = $2  [["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" = $1 AND "friendly_id_slugs"."sluggable_type" = $2  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" = $1  [["id", 47]]
  SQL (1.2ms)  DELETE FROM "posts" WHERE "posts"."id" = $1  [["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)

我该如何解决这个问题?

2 个答案:

答案 0 :(得分:0)

这个问题已经变得有点大,但我会试一试。 。

您的最新更新表明您的问题在于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年开始处理新记录的问题相关的讨论,其中可能包含一些可帮助您重构部分应用程序的信息,这些信息会给您带来麻烦:{{3} }

<强>更新

你已经回答了这个问题,但只是为了完成主义而注意到这里。 。

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

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

答案 1 :(得分:0)

我最终通过做一些事情来弄明白。将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

这似乎已经做到了。