问题
There is a typical blog application. Each user has_many posts. Each post has_many tags. I'm sorting each post by updated_at so the most recently updated post will show up on top. So for example, if I update a post's content, the post will come up to the top. However, this also happens when I just add a tag, since a tag is connected to its corresponding post.
I only want the content update to change updated_at field. I don't want updated_at for a post to be changed because I added a tag. Is there a way to do this? Or any other way to achieve something like this? Thank you!
回答1:
Two approaches spring to mind:
Don't use
:updated_at
for the purpose you are using it for. Instead create a new column, say:post_updated_at
, and update it manually on each save that you want to cause the post to move to the top. Rails provides a convenient model mehod for this:mypost.touch :post_updated_at
When you are updating a column and want
:updated_at
to remain untouched, use the #update_column method, which directly updates the column in the database with the value you give it. Note that it writes the value to the database verbatim, so you will have to be clever if the column in question is a fancyserialize
column or similar.
回答2:
Here's a replacement for save
which lets you blacklist "boring" attributes, ie those which don't trigger a timestamp update. It relies on the Rails 4+ update_columns
method to suppress callback methods during a save (be careful about suppressing those).
# Here we configure attribs that won't trigger the timestamp update
IGNORED_ATTRIBS = Set.new %w(reviewed_at view_count)
# A "save" replacement
def fussy_save
# Calculate any ignored attributes that are pending a save
all_changed_attribs = Set.new(self.changed_attributes.keys)
ignored_changed_attribs = IGNORED_ATTRIBS.intersection all_changed_attribs
# Save the ignored attributes without triggering updated_at
if ignored_changed_attribs.present?
self.update_columns self.attributes.slice(*ignored_changed_attribs)
end
# perform a normal save only if there are additional attributes present
if !all_changed_attribs.subset?(IGNORED_ATTRIBS)
save
end
end
Implementation note:
The final line above is a regular save. I initially tried doing a save without the check, on the basis that nothing should happen if the ignored attributes have already been saved (since save
does nothing, and doesn't update timestamp, if no attribs are saved). But I discovered that changed_attributes
is actually Rails' internal, definitive, reference, not just a convenience for developers (not too surprising :). It feels dangerous to start messing with that, so instead I just perform a check and all changed attributes will be saved in the SQL (including the ones just saved by update_columns), but only if there are non-ignored attributes present.
来源:https://stackoverflow.com/questions/10036807/how-to-update-a-models-updated-at-field-only-for-a-subset-of-column-updates