2016-05-30 10 views
2

Rails 4 und delayed_job 4.1.2. Ich versuche, die Neuberechnung der Gesamtbewertung nach dem Zerstören einer Überprüfung zu verzögern, aber offensichtlich, weil nach dem Zerstören eines Überprüfungsobjekts keine ID für das Überprüfungsobjekt vorhanden ist. Also jedes Mal, nachdem er versucht, ein Objekt zu zerstören, versucht es, eine verzögerte Job zu schaffen, sondern wirft diesen Fehler:Verzögerter Job - Job kann nicht für nicht persistenten Datensatz nach dem Destroy-Objekt erstellt werden

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' 

ich den folgenden Code haben:

# 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 

Es ist gut zu beachten, dass die calculate_overall_rating doesn benötige kein Review Objekt.

Wenn ich handle_asynchronously :calculate_overall_rating entfernen, wird es funktionieren, und neu berechnen. Aber ich versuche diesen Job zu verzögern.

Antwort

3

Dieser Fehler ist in der Tat raised von delayed_job, wenn Sie versuchen, eine Methode auf einem gelöschten (oder noch nicht erstellten) Datensatz zu verzögern. Die direkte Ursache dieses Fehlers ist, weil der verzögerte Job passes self (d. H. Das gerade gelöschte Überprüfungsobjekt) als das Zielobjekt beim Aufrufen der handle_asynchronously-Methode. Ich weiß nicht, warum es sich so verhält, ich habe gerade einen statement von einem der Autoren des Edelsteins gefunden, der sagt, dass es genauso funktioniert wie ActiveJob.

Wie auch immer, es scheint mir, dass Sie die Neuberechnungsmethode möglicherweise an der falschen Stelle definiert haben. Wenn ich es richtig verstehe, wird nach dem Zerstören einer Bewertung eine Durchschnittsbewertung basierend auf allen Bewertungen der gegebenen (z. B. ein Spot) neu berechnet. Es erscheint mir komisch, dass ein solcher Code als eine Methode einer einzigen Überprüfung Instanz definiert ist. Eine einzelne Rezension (was mehr eine gelöschte ist) sollte nichts über andere Überprüfungen desselben Spots wissen und sollte sich überhaupt nicht mit ihnen befassen müssen.

Ich denke, die Methode als Klasse Methode stattdessen definiert worden sein, mit dem reviewable als Parameter. Das würde natürlich bedeuten, dass Sie auch die anderen Berechnungsmethoden overall_rating und number_of_reviews, Klassenmethoden, machen müssen. Aber ich denke, das ist wieder eine gute Sache, denn die Domäne dieser Methoden liegt außerhalb eine einzige Überprüfung. So etwas wie die folgenden:

# 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 

Eine weitere Option (und Ich mag es sogar ein bisschen mehr schätze ich) wäre die Neuberechnung Methoden zu platzieren innerhalb der nachprüfbar Klasse, z.B. in der Post Klasse. Wenn Sie über mehr überprüfbare Klassentypen verfügen, können Sie ein Modul aus allen diesen Klassen einfügen, z. Reviewable (ich hoffe, dass das nicht mit dem Namen der Rails-Verbindung kollidieren würde) und setze die Neuberechnungsmethoden darin, diesmal als Instanz Methoden. Warum? Denn es handelt sich um eine Instanz eines überprüfbaren, die alle ihre Bewertungen neu berechnet bekommen möchte und die auch nach einer Review-Löschung noch existiert, so dass sie problemlos asynchron ausgeführt werden kann. So etwas wie das Folgende:

# 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