@changed_attributes trong việc log bằng PaperTrail
Bài đăng này đã không được cập nhật trong 8 năm
Trong việc log bằng PaperTrail
, đôi khi việc đưa thêm các gía trị mong muốn vào phần lưu log gặp khó khăn, bài viết sau đây hi vọng gíup đỡ phần nào công việc của bạn.
Link gem PaperTrail
https://github.com/airblade/paper_trail
Các model được sử dụng ở trong bài viết https://viblo.asia/pham.huy.cuong/posts/E7bGo9LOR5e2
Vậy ta có các bảng User, House, HousesUser
Thử update bản ghi đầu tiên của bảng User
trường name
và xem log của bản ghi này
irb(main):011:0> User.first.update name: "new_name"
irb(main):012:0> User.first.versions.last.object_changes
=> "---\nname:\n- name\n- new_name\nupdated_at:\n- 2016-04-08 09:17:04.741919000 Z\n- 2016-04-08 09:17:40.047259615 Z\n"
Chuyển thành hash
ta gọi changeset
irb(main):015:0> User.first.versions.last.changeset
=> {"name"=>["name", "new_name"], "updated_at"=>[2016-04-08 09:17:04 UTC, 2016-04-08 09:17:40 UTC]}
Bây giờ gỉa sử ta muốn đưa thêm trường version_index
lưu lại lượt thay đổi của record
vào object_changes
, ta thêm vào model User
before_save :create_version_index
private
def create_version_index
@changed_attributes ||= {}
@changed_attributes.merge! version_index: nil
end
def version_index
versions.count
end
Update lại trường name
ta thấy
irb(main):016:0> User.first.update name: "name"
irb(main):017:0> User.first.versions.last.changeset
=> {"name"=>["new_name", "name"], "version_index"=>[nil, 10], "updated_at"=>[2016-04-08 09:17:40 UTC, 2016-04-08 09:24:11 UTC]}
Thử gán lại trường name
nhưng chưa save ta sẽ hiểu rõ hơn về @changed_attributes
irb(main):018:0> user = User.first
irb(main):019:0> user.name = "new_name"
irb(main):020:0> user.changed_attributes
=> {"name"=>"name"}
irb(main):025:0> user.changes
=> {"name"=>["name", "new_name"]}
Trong PaperTrail
, hàm sinh ra object_changes
là changes_for_paper_trail
https://github.com/airblade/paper_trail/blob/master/lib/paper_trail/has_paper_trail.rb#L395
def changes_for_paper_trail
notable_changes = changes.delete_if { |k, _v| !notably_changed.include?(k) }
self.class.serialize_attribute_changes_for_paper_trail!(notable_changes)
notable_changes.to_hash
end
PaperTrail
dùng hàm changes
để lấy ra những attributes
thay đổi và gía trị cũ và mới của nó, hàm changes
gọi đến hàm changed_attributes
để tìm những attributes
có gía trị thay đổi.
def changed
changed_attributes.keys
end
def changed_attributes
@changed_attributes ||= ActiveSupport::HashWithIndifferentAccess.new
end
Ở đây ta thấy việc thay đổi @changed_attributes
sẽ gíup ta đưa thêm các gía trị muốn thêm vào hash
giữ các attributes
và các gía trị thay đổi của nó.
Đối với các attribute
thông thường thì việc đưa thêm value
là như vậy, còn với các mối quan hệ has_many
thì việc log sẽ như thế nào?, tại bài viết https://viblo.asia/pham.huy.cuong/posts/E7bGo9LOR5e2 đã trình bày cách log đối với dạng quan hệ này theo cách dùng cách tạo version
cho bảng trung gian HousesUser
, còn bằng cách sử dụng @changed_attributes
thì có thể làm theo cách sau:
Tại model
User
ta thay đổi 1 chút trong cách khai báo has_many
đối với houses
has_many :houses, through: :houses_users,
before_add: :create_association_params,
before_remove: :create_association_params
before_add, before_remove
là hàm call_back
để mỗi thi thêm hoặc bớt các mối quan hệ giữa 1 user
với 1 house
thì sẽ gọi đến hàm create_association_params
, thêm vào private
của model
User
:
def create_association_params record
@changed_attributes ||= {}
unless @changed_attributes[:house_ids]
@changed_attributes.merge! house_ids: old_house_ids
end
end
def old_house_ids
houses.pluck :id
end
Thử update house_ids
cho bản ghi đầu tiên của User
ta thấy:
irb(main):007:0> u = User.first
irb(main):008:0> u.update house_ids: [1,2]
irb(main):009:0> u.versions.last.changeset
=> {"house_ids"=>[[5, 6], [1, 2]], "updated_at"=>[2016-04-13 01:39:04 UTC, 2016-04-13 01:39:28 UTC], "version_index"=>[nil, 48]}
Chú ý house_ids
đã được log lại "house_ids"=>[[5, 6], [1, 2]]
.
Cảm ơn bạn đã đọc và hi vọng bài viết giúp ích phần nào công việc của bạn.
All rights reserved