Rails: 不调用回调更新模型属性

我有一个 User 模型,它有一个: credit 属性。我想要一个简单的按钮,通过一个名为“ add”的路由将用户的信用值增加5,这样/users/3/add 将使用户 id = 3的信用值增加5。

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

这是我的控制器的相关部分。问题是,我不想调用@user。因为我有一个 before _ save 回调函数,它根据当前 UTC 时间重新加密用户的密码。我只是想简单地向属性添加5并避免回调,我从未想过这么简单的事情会如此困难。

编辑:

我将回调更改为: before _ create, 下面是我的新控制器代码(相关部分) :

  def add
@user = User.find(params[:id])
@user.add_credits(5)
@user.save
flash[:success] = "Credits added!"
redirect_to root_path
end

这是我在模型中的代码:

 def add_credits(num)
self.credits = num
end

编辑2:

好吧,这是一个验证问题,使得“ EDIT”中的更改无法工作,但是我仍然希望能够回答最初的无回调更新问题!

81545 次浏览

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

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?.

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.

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

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

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

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.

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.

You can update a column doing this

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