Rails 4和delayed_job 4.1.2。我试图在销毁评论后推迟重新计算整体评级,但显然是因为在销毁评论对象后,Review对象没有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
,它将会起作用并重新计算。但我试图推迟这项工作。
答案 0 :(得分:4)
当您尝试在已删除(或尚未创建)的记录上延迟方法时,delayed_job确实会出现此错误raised。此错误的直接原因是,在调用handle_asynchronously
方法时,delayed_job passes self
(即刚刚删除的审阅对象)作为目标对象。我不知道为什么它会像那样,我刚从一位宝石作者那里找到一个statement,它说它与ActiveJob的工作方式相同。
无论如何,在我看来,你可能会在错误的地方定义重新计算方法。如果我理解正确,在销毁评论后,将根据给定reviewable
的所有评论(例如某个点)重新计算平均评分。对我来说这样的代码被定义为单个评论实例的方法似乎很奇怪。单个评论(更多是删除的评论)不应该对同一地点的其他评论有任何了解,也不应该对它们进行任何处理。
我想方法应该被定义为类方法而不是,reviewable
作为参数。当然,这意味着您必须制作其他计算方法overall_rating
和number_of_reviews
,类方法。但我认为这是一件好事,因为这些方法的领域在之外单一评论。如下所示:
# 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
另一种选择(我想我更喜欢它)将重新计算方法放在可审查类中,例如:在Post
类中。如果您有更多可审阅的类类型,则可以从所有这些类中包含一个模块,例如Reviewable
(我希望不会与Rails关联名称冲突)并将重新计算方法放在其中,这次是实例方法。为什么?因为它是一个可审阅的实例,它想要重新计算所有的评论,即使在删除评论后仍然存在,因此可以轻松地异步运行。如下所示:
# 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