Rails: Update model attribute without invoking callbacks

75,769

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
Share:
75,769

Related videos on Youtube

Sam Stern
Author by

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, 2020

Comments

  • Sam Stern
    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
      Dave Newton over 12 years
      I 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
    Sam Stern over 12 years
    Ok 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
    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
    Sam Stern over 12 years
    I would love to use this but I can't upgrade to Rails 3.1 cleanly, or at least I don't know how.
  • cvshepherd
    cvshepherd over 12 years
    Which Rails version are you running at the moment?
  • Jude Calimbas
    Jude Calimbas over 7 years
    its person.set(:name, "Robert Pulson")
  • Richard H Fung
    Richard H Fung about 7 years
    user.update_column credits:5 does not work. It should be user.update_column 'credits', 5
  • Matt
    Matt about 7 years
    I'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
    rept over 6 years
    @JudeCalimbas no it's not, ArgumentError: wrong number of arguments (given 2, expected 1)
  • Joshua Pinter
    Joshua Pinter over 3 years
    When using update_column, you can't pass it an Object like credits: 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 do update_column :credits, 5, which would generally be preferred over using a String for the column name.