Rails: Update model attribute without invoking callbacks

Ruby on-RailsRubyModelAttributesCallback

Ruby on-Rails Problem Overview


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!

Ruby on-Rails Solutions


Solution 1 - Ruby on-Rails

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 - Ruby on-Rails

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 - Ruby on-Rails

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 - Ruby on-Rails

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 - Ruby on-Rails

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

Solution 6 - Ruby on-Rails

You should be able to use update_all to avoid triggering callbacks.

def add
 @user = User.find(params[:id])
 User.where(:id=>@user.id).update_all(:credits => @user.credits+5)
 redirect_to root_path
end

I'd prefer to put this logic in the model, but this should work to solve your original problem as spec'd in the controller.

Solution 7 - Ruby on-Rails

You have a number of options, including changing which callback you use, e.g., after_create.

You can update columns without triggering callbacks, see Skipping Callbacks in the AR guide. For example, update_column doesn't trigger callbacks. The previous link lists non-triggering functions.

You could also use any of the Conditional Callback forms (or even an observer) for when the password is changed. See ActiveModel::Dirty, e.g., @user.password_changed?.

Solution 8 - Ruby on-Rails

Solution 9 - Ruby on-Rails

Maybe your other before_save hook should check if the user's password has actually changed before encrypting it again.

Solution 10 - Ruby on-Rails

You can update a column doing this

> User.where( name: 'lily' ).update_all(age: '10')

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionSam SternView Question on Stackoverflow
Solution 1 - Ruby on-RailscvshepherdView Answer on Stackoverflow
Solution 2 - Ruby on-RailsMattView Answer on Stackoverflow
Solution 3 - Ruby on-RailsMatt SmithView Answer on Stackoverflow
Solution 4 - Ruby on-Railshb922View Answer on Stackoverflow
Solution 5 - Ruby on-RailsDanneManneView Answer on Stackoverflow
Solution 6 - Ruby on-RailsmikedView Answer on Stackoverflow
Solution 7 - Ruby on-RailsDave NewtonView Answer on Stackoverflow
Solution 8 - Ruby on-RailskatzmopolitanView Answer on Stackoverflow
Solution 9 - Ruby on-RailsFinbarrView Answer on Stackoverflow
Solution 10 - Ruby on-RailsAbdul MonamView Answer on Stackoverflow