开发者

How to update a single attribute without touching the updated_at attribute?

开发者 https://www.devze.com 2023-02-08 03:27 出处:网络
How can I achieve this? tried to create 2 methods, called def disable_timestamps ActiveRecord::Base.record_timestamps = false

How can I achieve this?

tried to create 2 methods, called

def disable_timestamps
  ActiveRecord::Base.record_timestamps = false
end

def enable_timestamps
  ActiveRecord::Base.record_timestamps = true
end

and the update method itself:

def increment_pagehit
  update_开发者_C百科attribute(:pagehit, pagehit+1)
end

turn timestamps on and off using callbacks like:

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

but it's not updating anything, even the desired attribute (pagehit).

Any advice? I don't want to have to create another table just to count the pagehits.


As an alternative to update_attribute, In Rails 3.1+ you can use update_column.

update_attribute skips validations, but will touch updated_at and execute callbacks.

update_column skips validations, does not touch updated_at, and does not execute callbacks.

Thus, update_column is a great choice if you don't want to affect updated_at and don't need callbacks.

See http://api.rubyonrails.org/classes/ActiveRecord/Persistence.html for more information.

Also note that update_column will update the value of the attribute in the in-memory model and it won't be marked as dirty. For example:

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


If all you're wanting to do is increment a counter, I'd use the increment_counter method instead:

ModelName.increment_counter :pagehit, id


Is there a way to avoid automatically updating Rails timestamp fields?

Or closer to your question:

http://blog.bigbinary.com/2009/01/21/override-automatic-timestamp-in-activerecord-rails.html


it is not a good idea to do this:

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

it should be

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

the reason is if two requests are parallel, on the first version both will update the pagehits with the same number, as it uses the number saved in the Ruby memory. The second option uses the sql server to increase the number by 1, in case two of these queries come at the same time, the server will process them one after the other, and will end up with the correct number of pagehits.


To avoid Monkeypatchingtroubles you could also use ModelName.update_all for this purpose:

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

This also does not touch the timestamps on the record.


You also have decrement and increment (and their bang versions) which do not alter updated_at, do not go trigger validation callbacks and are obviously handy for counters / integers.


If precision is not really that important, and you don't expect the code to run many times, you can try altering the saved in the database updated_at value, like so:

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

so that Rails will actually try to save the same value, and not replace it with Time.now.

0

精彩评论

暂无评论...
验证码 换一张
取 消