Ruby-on-rails Rails:在不调用回调的情况下更新模型属性
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/8650983/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Rails: Update model attribute without invoking callbacks
提问by Sam Stern
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.
我有一个具有 :credits 属性的 User 模型。我想要一个简单的按钮,它将通过名为“add”的路由为用户的信用添加 5,以便 /users/3/add 将向用户 id = 3 的信用添加 5。
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.
那是我的控制器的相关部分。问题是,我不想调用 @user.save 因为我有一个 before_save 回调,它根据当前的 UTC 时间重新加密用户的密码。我只想简单地给属性加5,避免回调,没想到这么简单的事情会这么难。
EDIT:
编辑:
I changed the callback to :before_create, here is my new controller code (relevant part):
我将回调更改为:before_create,这是我的新控制器代码(相关部分):
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:
编辑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!
好的,这是一个验证问题,导致“编辑”中的更改不起作用,但我仍然希望回答原始问题的更新而无需回调!
回答by cvshepherd
Rails 3.1 introduced update_column, which is the same as update_attribute, but without triggering validations or callbacks:
Rails 3.1 引入了update_column,与 相同update_attribute,但不触发验证或回调:
http://apidock.com/rails/ActiveRecord/Persistence/update_column
http://apidock.com/rails/ActiveRecord/Persistence/update_column
回答by Matt
As a general answer, in Rails 4 this is a simple way to update attributes without triggering callbacks:
作为一般答案,在 Rails 4 中,这是一种无需触发回调即可更新属性的简单方法:
@user.update_column 'credits', 5
If you need to update multiple attributes without triggers callbacks:
如果您需要在不触发回调的情况下更新多个属性:
@user.update_columns credits: 5, bankrupt: false
There are other options here in the Rails Guidesif you prefer, but I found this way to be the easiest.
如果您愿意,Rails 指南中还有其他选项,但我发现这种方式最简单。
回答by Matt Smith
To update multiple attributes without callbacks you can use update_all in your model as so:
要在没有回调的情况下更新多个属性,您可以在模型中使用 update_all ,如下所示:
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.
如果您真的想要,您甚至可以尝试使用 update_columns 方法并将其混合到您的活动记录基类中。
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
要更新一个属性,您可以使用 update_column。此外,在 Rails 指南中可以找到一些特定的方法http://guides.rubyonrails.org/active_record_callbacks.html#skiping-callbacks
回答by DanneManne
I think you should use the method update_countersin this case. Use it like this in your controller action:
我认为在这种情况下您应该使用update_counters方法。在您的控制器操作中像这样使用它:
def add
User.update_counters params[:id], :credits => 5
redirect_to root_path
end
回答by hb922
For mongoid, I ended up using http://mongoid.org/en/mongoid/docs/persistence.htmlSpecifically, you can use:
对于 mongoid,我最终使用了http://mongoid.org/en/mongoid/docs/persistence.html具体来说,你可以使用:
person.set(name:"Robert Pulson")
and no callback will be issued. So cool.
并且不会发出回调。非常酷。
回答by miked
You should be able to use update_allto avoid triggering callbacks.
您应该能够使用update_all来避免触发回调。
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.
我更愿意将此逻辑放在模型中,但这应该可以解决控制器中规范的原始问题。
回答by katzmopolitan
A few options for how to do this in rails4 http://edgeguides.rubyonrails.org/active_record_callbacks.html#skipping-callbacks
关于如何在 rails4 中执行此操作的一些选项http://edgeguides.rubyonrails.org/active_record_callbacks.html#skiping-callbacks
回答by Finbarr
Maybe your other before_save hook should check if the user's password has actually changed before encrypting it again.
也许你的另一个 before_save 钩子应该在再次加密之前检查用户的密码是否真的改变了。
回答by Dave Newton
You have a number of options, including changing which callback you use, e.g., after_create.
您有多种选择,包括更改您使用的回调,例如,after_create.
You can update columns without triggering callbacks, see Skipping Callbacksin the AR guide. For example, update_columndoesn't trigger callbacks. The previous link lists non-triggering functions.
您可以在不触发回调的情况下更新列,请参阅AR 指南中的跳过回调。例如,update_column不触发回调。上一个链接列出了非触发函数。
You could also use any of the Conditional Callbackforms (or even an observer) for when the password is changed. See ActiveModel::Dirty, e.g., @user.password_changed?.
您还可以在更改密码时使用任何条件回调表单(甚至是观察者)。参见ActiveModel::Dirty,例如,@user.password_changed?。
回答by Abdul Monam
You can update a column doing this
你可以更新一列这样做
User.where( name: 'lily' ).update_all(age: '10')
User.where( name: 'lily' ).update_all(age: '10')

