如何在不触摸updated_at属性的情况下更新单个属性?

时间:2011-02-03 14:53:00

标签: ruby-on-rails ruby ruby-on-rails-3

我怎样才能做到这一点?

试图创建两个名为

的方法
def disable_timestamps
  ActiveRecord::Base.record_timestamps = false
end

def enable_timestamps
  ActiveRecord::Base.record_timestamps = true
end

和更新方法本身:

def increment_pagehit
  update_attribute(:pagehit, pagehit+1)
end

使用如下回调来打开和关闭时间戳:

before_update :disable_timestamps, :only => :increment_pagehit
after_update :enable_timestamps, :only => :increment_pagehit

但它没有更新任何东西,甚至是所需的属性(pagehit)。

有什么建议吗?我不想创建另一个表来计算分页数。

7 个答案:

答案 0 :(得分:90)

作为update_attribute的替代方案,在Rails 3.1+中,您可以使用update_column

update_attribute会跳过验证,但会触及updated_at并执行回调。

update_column跳过验证,不会触及updated_at,也不会执行回调。

因此,如果您不想影响update_column并且不需要回调,updated_at是一个很好的选择。

有关详细信息,请参阅http://api.rubyonrails.org/classes/ActiveRecord/Persistence.html

另请注意,update_column将更新内存模型中属性的值,并且不会将其标记为脏。例如:

p = Person.new(:name => "Nathan")
p.save
p.update_column(:name, "Andrew")
p.name == "Andrew" # True
p.name_changed? # False

答案 1 :(得分:12)

答案 2 :(得分:12)

如果您只想增加一个计数器,我会改用increment_counter方法:

ModelName.increment_counter :pagehit, id

答案 3 :(得分:2)

为避免Monkeypatchingtroubles,你也可以使用ModelName.update_all:

def increment_pagehit
  self.class.update_all({ pagehit: pagehit+1 }, { id: id })
end

这也不会触及记录上的时间戳。

答案 4 :(得分:2)

这样做不是一个好主意:

self.class.update_all({ pagehit: pagehit+1 }, { id: id })

应该是

self.class.update_all("pagehit = pagehit + 1", { id: id })

原因是如果两个请求是并行的,则在第一个版本上,两个请求都将使用相同的数字更新pagehits,因为它使用保存在Ruby内存中的数字。第二个选项使用sql server将数字增加1,如果其中两个查询同时出现,服务器将一个接一个地处理它们,并最终得到正确的分页数。

答案 5 :(得分:1)

您还有decrementincrement(以及他们的爆炸版本)不会改变updated_at,不会触发验证回调,显然对计数器/整数很方便。

答案 6 :(得分:0)

如果精确度不是那么重要,并且您不希望代码多次运行,您可以尝试更改数据库updated_at中保存的值,如下所示:

u = User.first
u.name = "Alex 2" # make some changes...
u.updated_at = u.updated_at + 0.000001.second # alter updated_at
u.save

这样Rails实际上会尝试保存相同的值,而不是用Time.now替换它。