Rails: Update model attribute without invoking callbacks
Solution 1
Rails 3.1 introduced update_column
, which is the same as update_attribute
, but without triggering validations or callbacks:
http://apidock.com/rails/ActiveRecord/Persistence/update_column
Solution 2
As a general answer, in Rails 4 this is a simple way to update attributes without triggering callbacks:
@user.update_column :credits, 5
If you need to update multiple attributes without triggering callbacks:
@user.update_columns credits: 5, bankrupt: false
There are other options here in the Rails Guides if you prefer, but I found this way to be the easiest.
Solution 3
To update multiple attributes without callbacks you can use update_all in your model as so:
self.class.update_all({name: value, name: value}, self.class.primary_key => id)
If you really want you can even try even a update_columns method and mixin this to your active record base class.
To update one attribute you can use update_column. In addition there is some specific methods that can found in the rails guides http://guides.rubyonrails.org/active_record_callbacks.html#skipping-callbacks
Solution 4
For mongoid, I ended up using http://mongoid.org/en/mongoid/docs/persistence.html Specifically, you can use:
person.set(name:"Robert Pulson")
and no callback will be issued. So cool.
Solution 5
I think you should use the method update_counters in this case. Use it like this in your controller action:
def add
User.update_counters params[:id], :credits => 5
redirect_to root_path
end
Related videos on Youtube
Sam Stern
Developer Programs Engineer at Google, working on Firebase and all things Android. Also occasionally hack on JavaScript, Ruby, Scala, Haskell. (former username hatboysam)
Updated on November 27, 2020Comments
-
Sam Stern over 3 years
I have a User model that has a :credits attribute. I want a simple button that will add 5 to the user's credits, through a route called "add" so that /users/3/add would add 5 to the credits of user id = 3.
def add @user = User.find(params[:id]) @user.credits += 5 redirect_to root_path end
That is the relevant part of my controller. The problem is, I dont want to call @user.save because I have a before_save callback that re-encrypts the user's password based on the current UTC time. I just want to simply add 5 to the attribute and avoid the callback, I never thought such a simple thing could be so hard.
EDIT:
I changed the callback to :before_create, here is my new controller code (relevant part):
def add @user = User.find(params[:id]) @user.add_credits(5) @user.save flash[:success] = "Credits added!" redirect_to root_path end
and here is my code in the model:
def add_credits(num) self.credits = num end
EDIT 2:
Ok it was a validation problem that made the changes in "EDIT" not work, but I'd still love an answer to the original question of updating without callbacks!
-
Dave Newton over 12 yearsI provided a link with a list of the methods that don't trigger callbacks, and both Finbarr and I suggested using a conditional callback--what additional solutions are you looking for?
-
-
Sam Stern over 12 yearsOk I changed it to "after_create" but it still won't work! See my edit for my new code, I can't get credits to change.
-
Dave Newton over 12 years@hatboysam Well, it does something different--before you were adding, now you're setting. Use
save!
to see if there's an exception (and/or check the logs, and/or remove the backtrace silencers). -
Sam Stern over 12 yearsI would love to use this but I can't upgrade to Rails 3.1 cleanly, or at least I don't know how.
-
cvshepherd over 12 yearsWhich Rails version are you running at the moment?
-
Jude Calimbas over 7 yearsits person.set(:name, "Robert Pulson")
-
Richard H Fung about 7 yearsuser.update_column credits:5 does not work. It should be user.update_column 'credits', 5
-
Matt about 7 yearsI'm using Rails 4.2 and this definitely works for me. This line generates the following query:
UPDATE "users" SET "credits" = $1, "updated_at" = $2 WHERE "users"."id" = $3 [["credits", 5], ["updated_at", "2017-04-01 21:34:52.746626"], ["id", 1]]
-
rept over 6 years@JudeCalimbas no it's not, ArgumentError: wrong number of arguments (given 2, expected 1)
-
Joshua Pinter over 3 yearsWhen using
update_column
, you can't pass it an Object likecredits: 5
. You have to pass two parameters, the first being the column name and the second being the value to update it to. So you can doupdate_column :credits, 5
, which would generally be preferred over using a String for the column name.