How to update a model's “updated_at” field only for a subset of column updates?

百般思念 提交于 2020-01-02 05:24:06

问题


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:

  1. 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
    
  2. 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 fancy serialize 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!