Reputation: 14504
I want to create an after_save method for my rate action. It would divide rating_score/ratings and update the column rating.
class KonkurrancersController < ApplicationController
def rate
@konkurrancer = Konkurrancer.find(params[:id])
@container = "Konkurrancer"[email protected]_s
@konkurrancer.rating_score += params[:vind][:rating].to_i
@konkurrancer.ratings += 1
@konkurrancer.save
respond_to do |format|
format.js
end
end
end
This is my model:
class Konkurrancer < ActiveRecord::Base
after_save :do_foobar
private
def do_foobar
rating_score = self.rating_score
ratings = self.ratings
rating = (rating_score/ratings)
self.update_attributes(:rating => rating)
end
end
My rails log:
Started POST "/konkurrancers/rate/46" for 127.0.0.1 at 2011-04-26 23:40:56 +0200
Processing by KonkurrancersController#rate as */*
Parameters: {"utf8"=>"Ô£ô", "authenticity_token"=>"MACFM37hX4S6XA9vryn7gtfl21P
vcaPBSiKDI8mfurg=", "vind"=>{"rating"=>"4"}, "id"=>"46"}
←[1m←[36mKonkurrancer Load (1.0ms)←[0m ←[1mSELECT `konkurrancers`.* FROM `kon
kurrancers`←[0m
←[1m←[35mCACHE (0.0ms)←[0m SELECT `konkurrancers`.* FROM `konkurrancers`
←[1m←[36mCACHE (0.0ms)←[0m ←[1mSELECT `konkurrancers`.* FROM `konkurrancers`←
[0m
←[1m←[35mKonkurrancer Load (1.0ms)←[0m SELECT `konkurrancers`.* FROM `konkurr
ancers` WHERE (`konkurrancers`.`cached_slug` = '46') LIMIT 1
←[1m←[36mSQL (2.0ms)←[0m ←[1mSELECT sluggable_id FROM slugs WHERE ((slugs.slu
ggable_type = 'Konkurrancer' AND slugs.name = '46' AND slugs.sequence = 1))←[0m
←[1m←[35mKonkurrancer Load (1.0ms)←[0m SELECT `konkurrancers`.* FROM `konkurr
ancers` WHERE (`konkurrancers`.`id` = 46) LIMIT 1
←[1m←[36mSQL (0.0ms)←[0m ←[1mBEGIN←[0m
←[1m←[35mLink Load (1.0ms)←[0m SELECT `links`.* FROM `links` WHERE (`links`.k
onkurrancer_id = 46) LIMIT 1
←[1m←[36mSQL (0.0ms)←[0m ←[1mROLLBACK←[0m
Rendered konkurrancers/_rating.html.erb (1.0ms)
Rendered konkurrancers/rate.js.erb (22.0ms)
Completed 200 OK in 606ms (Views: 286.0ms | ActiveRecord: 6.0ms)
How should I create this?
Upvotes: 15
Views: 33038
Reputation: 161
Many of the answers on this post might have been correct but are out of date now. The Rails docs suggests assigning values directly (self.attribute = 'value'
) instead of updating or saving attributes (update(attribute: 'value'
) in callbacks.
after_save :do_foobar
private
def do_foobar
self.attribute = "value"
end
.update_column
)Safe because it will prevent additional callbacks from being triggered but not suggested as it can have unintended side effects.
after_save :do_foobar
private
def do_foobar
update_column(:attribute, "value")
end
.update
)Can trigger subsequent callbacks infinitely.
after_save :do_foobar
private
def do_foobar
update(attribute: "value")
end
Upvotes: 1
Reputation: 2966
Any update_attribute
in an after_save
callback will cause recursion, in Rails3+.
What should be done is:
after_save :updater
# Awesome Ruby code
# ...
# ...
private
def updater
self.update_column(:column_name, new_value) # This will skip validation gracefully.
end
Upvotes: 81
Reputation: 19723
what you want is a callback. You can create an after_save callback on your Konkurrancer model, which triggers after the save() method is called for that model.
For example:
class Konkurrancer < ActiveRecord::Base
after_save :do_foobar
private
def do_foobar
rating_score = self.rating_score
ratings = self.ratings
rating = (rating_score/ratings)
self.update_attributes(:ratings => rating)
end
end
[EDIT] You should use self, since the model you are editing is the model itself. Test it out, and apply necessary logic/implementation.
Have a look at this guide for more info.
Hope that helps!
Upvotes: 9