Rails:更新模型属性而不调用回调
Posted
技术标签:
【中文标题】Rails:更新模型属性而不调用回调【英文标题】:Rails: Update model attribute without invoking callbacks 【发布时间】:2012-01-28 21:00:21 【问题描述】:我有一个具有 :credits 属性的用户模型。我想要一个简单的按钮,通过一个名为“add”的路由为用户的信用添加 5,以便 /users/3/add 为用户 id = 3 的信用添加 5。
def add
@user = User.find(params[:id])
@user.credits += 5
redirect_to root_path
end
这是我的控制器的相关部分。问题是,我不想调用@user.save,因为我有一个 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:
好的,这是一个验证问题,导致“编辑”中的更改不起作用,但我仍然希望能回答原始问题的答案,即在没有回调的情况下进行更新!
【问题讨论】:
我提供了一个链接,其中包含不触发回调的方法列表,Finbarr 和我都建议使用条件回调——您还在寻找哪些其他解决方案? 【参考方案1】:作为一般答案,在 Rails 4 中,这是一种无需触发回调即可更新属性的简单方法:
@user.update_column :credits, 5
如果需要在不触发回调的情况下更新多个属性:
@user.update_columns credits: 5, bankrupt: false
如果您愿意,还有其他选项 here in the Rails Guides,但我发现这种方式最简单。
【讨论】:
user.update_column credits:5 不起作用。它应该是 user.update_column 'credits', 5 我使用的是 Rails 4.2,这绝对适合我。此行生成以下查询: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]]
当使用update_column
时,你不能传递一个像credits: 5
这样的Object。您必须传递两个参数,第一个是列名,第二个是要更新的值。所以你可以使用update_column :credits, 5
,这通常比使用字符串作为列名更受欢迎。【参考方案2】:
您可以这样做更新列
User.where(name: 'lily').update_all(age: '10')
【讨论】:
【参考方案3】:要在不使用回调的情况下更新多个属性,您可以在模型中使用 update_all,如下所示:
self.class.update_all(name: value, name: value, self.class.primary_key => id)
如果您真的想要,您甚至可以尝试使用 update_columns 方法并将其混合到您的活动记录基类中。
要更新一个属性,您可以使用 update_column。另外还有一些具体的方法可以在rails guideshttp://guides.rubyonrails.org/active_record_callbacks.html#skipping-callbacks找到
【讨论】:
【参考方案4】:对于 mongoid,我最终使用了 http://mongoid.org/en/mongoid/docs/persistence.html 具体来说,您可以使用:
person.set(name:"Robert Pulson")
并且不会发出回调。太酷了。
【讨论】:
它的 person.set(:name, "Robert Pulson") @JudeCalimbas 不,不是,ArgumentError:参数数量错误(给定 2,预期 1)【参考方案5】:您有多种选择,包括更改您使用的回调,例如,after_create
。
您可以在不触发回调的情况下更新列,请参阅 AR 指南中的 Skipping Callbacks。例如,update_column
不会触发回调。上一个链接列出了非触发函数。
您还可以在更改密码时使用任何Conditional Callback 表单(甚至是观察者)。参见ActiveModel::Dirty,例如@user.password_changed?
。
【讨论】:
好吧,我把它改成了“after_create”,但还是不行!请参阅我对新代码的编辑,我无法更改积分。 @hatboysam 好吧,它做了一些不同的事情——在你添加之前,现在你正在设置。使用save!
查看是否有异常(和/或检查日志,和/或删除回溯消音器)。【参考方案6】:
如何在 rails4 http://edgeguides.rubyonrails.org/active_record_callbacks.html#skipping-callbacks 中执行此操作的一些选项
【讨论】:
【参考方案7】:Rails 3.1 引入了update_column
,与update_attribute
相同,但不触发验证或回调:
http://apidock.com/rails/ActiveRecord/Persistence/update_column
【讨论】:
我很想使用它,但我无法干净地升级到 Rails 3.1,或者至少我不知道如何升级。 您目前运行的是哪个 Rails 版本?【参考方案8】:我认为在这种情况下你应该使用方法update_counters。在您的控制器操作中像这样使用它:
def add
User.update_counters params[:id], :credits => 5
redirect_to root_path
end
【讨论】:
【参考方案9】:您应该能够使用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
我更愿意将此逻辑放入模型中,但这应该可以解决您在控制器中指定的原始问题。
【讨论】:
【参考方案10】:也许你的另一个 before_save 钩子应该在再次加密之前检查用户的密码是否真的改变了。
【讨论】:
以上是关于Rails:更新模型属性而不调用回调的主要内容,如果未能解决你的问题,请参考以下文章
Rails Rspec / Factory Bot没有调用模型before_save回调
如何向 Rails 4 中的所有 ActiveRecord 模型添加 `audited` 方法调用?