2016-05-30 7 views
2

Rails 4 и delayed_job 4.1.2. Я пытаюсь задержать перерасчет общего рейтинга после уничтожения обзора, но очевидно, что после уничтожения объекта обзора нет идентификатора объекта Review. Поэтому каждый раз после попытки уничтожить объект, он пытается создать замедленную работу, но выдает эту ошибку:Задержка задания - задание не может быть создано для записи без сохранения после уничтожения объекта

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 Безразлично 't требуется объект Review.

Если я удалю handle_asynchronously :calculate_overall_rating, он будет работать и пересчитывать. Но я пытаюсь отложить эту работу.

ответ

3

Эта ошибка действительно raised by delayed_job при попытке задержать метод в удаленной (или еще не созданной) записи. Прямой причиной этой ошибки является то, что delayed_job passes self (т. Е. Только что удаленный объект обзора) в качестве целевого объекта при вызове метода handle_asynchronously. Я не знаю, почему так ведет себя, я только что нашел 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 

 Смежные вопросы

  • Нет связанных вопросов^_^