延迟作业 - 销毁对象后无法为非持久记录创建作业
Delayed Job - job cannot be created for non-persisted record after destroy object
Rails 4 和 delayed_job 4.1.2。我试图在销毁评论后延迟重新计算总体评分,但显然是因为在销毁评论对象后没有评论对象的 ID。所以每次在尝试销毁一个对象后,它都会尝试创建一个延迟的作业但会抛出这个错误:
ArgumentError (job cannot be created for non-persisted record:
#<Review id: 44, review: "Bad", rating: 1, reviewable_id: 2,
reviewable_type: "Spot", user_id: 1, created_at: "2016-05-30 17:13:29",
updated_at: "2016-05-30 17:13:29">):
app/controllers/reviews_controller.rb:40:in `destroy'
我有以下代码:
# reviews_controller.rb
class ReviewsController < ApplicationController
def destroy
review.destroy
flash[:success] = t("reviews.destroy.success")
end
end
# review.rb
class Review < ActiveRecord::Base
after_destroy :calculate_overall_rating
def calculate_overall_rating
if number_of_reviews > 0
reviewable.update_attribute(:overall_rating, overall_rating)
else
reviewable.update_attribute(:overall_rating, 0)
end
end
handle_asynchronously :calculate_overall_rating
end
值得注意的是 calculate_overall_rating
不需要 Review
对象。
如果我删除 handle_asynchronously :calculate_overall_rating
它将起作用,并重新计算。但是我想推迟这份工作。
这个错误确实 raised by delayed_job when you try to delay a method on a deleted (or not yet created) record. The direct cause of this error is because delayed_job passes self
(i.e. the just-deleted review object) as the target object when calling thehandle_asynchronously
method. I don't know why it behaves like that, I just found a statement 来自 gem 的一位作者,他说它的工作方式与 ActiveJob 相同。
无论如何,在我看来,您可能在错误的地方定义了重新计算方法。如果我理解正确的话,在销毁评论后,将根据给定 reviewable
(例如一个点)的所有评论重新计算平均评分。我觉得这样的代码被定义为单次审查实例的方法似乎很奇怪。单个评论(更何况是被删除的评论)不应该对同一地点的其他评论一无所知,根本不必处理它们。
我想 方法应该被定义为 class 方法而不是 ,reviewable
作为参数。当然,这意味着您还必须使其他计算方法 overall_rating
和 number_of_reviews
、class 方法。但我再次认为这是一件好事,因为这些方法的领域位于 单个评论之外。类似于以下内容:
# review.rb
class Review < ActiveRecord::Base
after_destroy :recalculate_overall_rating
def recalculate_overall_rating
self.class.calculate_overall_rating(reviewable)
end
def self.calculate_overall_rating(reviewable)
if number_of_reviews(reviewable) > 0
reviewable.update_attribute(:overall_rating, overall_rating(reviewable))
else
reviewable.update_attribute(:overall_rating, 0)
end
end
handle_asynchronously :calculate_overall_rating
end
另一种选择(我想我更喜欢它)是将重新计算方法 放在可审查的 class 中,例如在Post
class里面。如果您有更多可审查的 class 类型,您可以制作一个包含所有这些 class 的模块,例如Reviewable
(我希望不会与 Rails 关联名称冲突)并将重新计算方法放入其中,这次是 instance 方法。为什么?因为它是一个 instance 的 reviewable,它想要重新计算它的所有评论,并且即使在评论删除后它仍然存在,所以它可以很容易地 运行 异步。类似于以下内容:
# reviewable.rb
module Reviewable
def calculate_overall_rating
if number_of_reviews > 0
update_attribute(:overall_rating, overall_rating)
else
update_attribute(:overall_rating, 0)
end
end
handle_asynchronously :calculate_overall_rating
# overall_rating and number_of_reviews are also defined in this module
end
# review.rb
class Review < ActiveRecord::Base
after_destroy :recalculate_overall_rating
def recalculate_overall_rating
reviewable.calculate_overall_rating
end
end
# post.rb
class Post < ActiveRecord::Base
include Reviewable
end
Rails 4 和 delayed_job 4.1.2。我试图在销毁评论后延迟重新计算总体评分,但显然是因为在销毁评论对象后没有评论对象的 ID。所以每次在尝试销毁一个对象后,它都会尝试创建一个延迟的作业但会抛出这个错误:
ArgumentError (job cannot be created for non-persisted record:
#<Review id: 44, review: "Bad", rating: 1, reviewable_id: 2,
reviewable_type: "Spot", user_id: 1, created_at: "2016-05-30 17:13:29",
updated_at: "2016-05-30 17:13:29">):
app/controllers/reviews_controller.rb:40:in `destroy'
我有以下代码:
# reviews_controller.rb
class ReviewsController < ApplicationController
def destroy
review.destroy
flash[:success] = t("reviews.destroy.success")
end
end
# review.rb
class Review < ActiveRecord::Base
after_destroy :calculate_overall_rating
def calculate_overall_rating
if number_of_reviews > 0
reviewable.update_attribute(:overall_rating, overall_rating)
else
reviewable.update_attribute(:overall_rating, 0)
end
end
handle_asynchronously :calculate_overall_rating
end
值得注意的是 calculate_overall_rating
不需要 Review
对象。
如果我删除 handle_asynchronously :calculate_overall_rating
它将起作用,并重新计算。但是我想推迟这份工作。
这个错误确实 raised by delayed_job when you try to delay a method on a deleted (or not yet created) record. The direct cause of this error is because delayed_job passes self
(i.e. the just-deleted review object) as the target object when calling thehandle_asynchronously
method. I don't know why it behaves like that, I just found a statement 来自 gem 的一位作者,他说它的工作方式与 ActiveJob 相同。
无论如何,在我看来,您可能在错误的地方定义了重新计算方法。如果我理解正确的话,在销毁评论后,将根据给定 reviewable
(例如一个点)的所有评论重新计算平均评分。我觉得这样的代码被定义为单次审查实例的方法似乎很奇怪。单个评论(更何况是被删除的评论)不应该对同一地点的其他评论一无所知,根本不必处理它们。
我想 方法应该被定义为 class 方法而不是 ,reviewable
作为参数。当然,这意味着您还必须使其他计算方法 overall_rating
和 number_of_reviews
、class 方法。但我再次认为这是一件好事,因为这些方法的领域位于 单个评论之外。类似于以下内容:
# review.rb
class Review < ActiveRecord::Base
after_destroy :recalculate_overall_rating
def recalculate_overall_rating
self.class.calculate_overall_rating(reviewable)
end
def self.calculate_overall_rating(reviewable)
if number_of_reviews(reviewable) > 0
reviewable.update_attribute(:overall_rating, overall_rating(reviewable))
else
reviewable.update_attribute(:overall_rating, 0)
end
end
handle_asynchronously :calculate_overall_rating
end
另一种选择(我想我更喜欢它)是将重新计算方法 放在可审查的 class 中,例如在Post
class里面。如果您有更多可审查的 class 类型,您可以制作一个包含所有这些 class 的模块,例如Reviewable
(我希望不会与 Rails 关联名称冲突)并将重新计算方法放入其中,这次是 instance 方法。为什么?因为它是一个 instance 的 reviewable,它想要重新计算它的所有评论,并且即使在评论删除后它仍然存在,所以它可以很容易地 运行 异步。类似于以下内容:
# reviewable.rb
module Reviewable
def calculate_overall_rating
if number_of_reviews > 0
update_attribute(:overall_rating, overall_rating)
else
update_attribute(:overall_rating, 0)
end
end
handle_asynchronously :calculate_overall_rating
# overall_rating and number_of_reviews are also defined in this module
end
# review.rb
class Review < ActiveRecord::Base
after_destroy :recalculate_overall_rating
def recalculate_overall_rating
reviewable.calculate_overall_rating
end
end
# post.rb
class Post < ActiveRecord::Base
include Reviewable
end