关于the paper_trail gem的问题。
当只有关联更改时,不会为主模型创建版本记录。那么列出某个记录的所有版本(包括其关联)的正确方法是什么?
我应该查询这样的内容吗? (不好的一点是这个SQL查询可能性能很长而且性能很低。)
f = "(item_type = 'Place' AND item_id = ?) OR (item_type = 'PlaceName' AND item_id IN (?))"
PaperTrail::Version.where(f, @place.id, @place.names.map { |n| n.id })
或者我应该在只更改关联时创建版本记录吗?我想@DavidHam尝试了同样的事情并问a similar question但是还没有人回答。
答案 0 :(得分:2)
所以,我找到了一种方法来做到这一点,但它并不完全漂亮,并且每次更改关联时都不会创建新版本。但是,它确实为您提供了一种按时间顺序检索版本的有效方法,以便您可以在关联更改之前/之后查看版本的外观。
首先,在给定该模型的id的情况下,检索所有asscoiation版本的ID:
def associations_version_ids(item_id=nil)
if !item_id.nil?
ids = PaperTrail::VersionAssociation.where(foreign_key_id: item_id, foreign_key_name: 'item_id').select(:version_id)
return ids
else
ids = PaperTrail::VersionAssociation.where(foreign_key_name: 'item_id').select(:version_id)
return ids
end
end
然后我使用此函数中的VersionAssociation id将所有版本放在一起。它将返回按时间顺序排列的PaperTrail :: Version' s。因此,这些信息对于活动日志等非常有用。通过这种方式将版本及其关联重新组合在一起非常简单:
def all_versions
if !@item_id.nil?
association_version_ids = associations_version_ids(@item_id)
all_versions = PaperTrail::Version
.where("(item_type = ? AND item_id = ?) OR id IN (?)", 'Item', @item_id, association_version_ids)
.where("object_changes IS NOT NULL")
.order(created_at: :desc)
return all_versions
else
assocation_ids = associations_version_ids
all_versions = PaperTrail::Version
.where("item_type = ? OR id IN (?)", 'Item', association_ids)
.where("object_changes IS NOT NULL")
.order(created_at: :desc)
return all_versions
end
end
同样,不是一个完美的答案,因为每次有变化都没有新版本,但它是可管理的。
答案 1 :(得分:1)
这更像是一种方法,而不是具体的答案,但是这里有。
在我的情况下,我需要一个版本历史记录,以便每当有人更改Child
时,他们也会更改“父级”上的标记。但是我需要一种方法来显示一条审计线索,该线索将显示所有孩子的初始值,以及每当有人改变孩子时父母的审计线。
所以,非常简化,就像这样:
class Parent < ActiveRecord::Base
has_paper_trail
has_many :children
end
class Child < ActiveRecord::Base
has_paper_trail
belongs_to :parent
end
因此,只要Child
发生变更,我们就需要在Parent
上创建一个版本。
首先,尝试更改Child
,如下所示:
class Child < ActiveRecord::Base
has_paper_trail
belongs_to :parent, touch: true
end
这应该(应该!未经测试)在Parent
更改时Child
创建时间戳。
然后,要获取每个:children
版本的Parent
状态,请在Child
的版本中搜索transaction_id
与Parent
匹配的版本{1}}。
# loop through the parent versions
@parent.versions.each do |version|
@parent.children.versions.each do |child|
# Then loop through the children and get the version of the Child where the transaction_id matches the given Parent version
child_version = child.versions.find_by transaction_id: version.transaction_id
if child_version # it will only exist if this Child changed in this Parent's version
# do stuff with the child's version
end
这在我的情况下起作用,希望这里的某些内容对你有用。
答案 2 :(得分:1)
[增订]
我找到了更好的方法。您需要更新事务内的关联以使此代码有效。
23 .09
[原始答案]
这是我迄今为止发现的最佳方式。 (@ JohnSchaum的回答对我很有帮助,谢谢!)
在开始之前,我已将class Place < ActiveRecord::Base
has_paper_trail
before_update :check_update
def check_update
return if changed_notably?
tracking_has_many_associations = [ ... ]
tracking_has_has_one_associations = [ ... ]
tracking_has_many_associations.each do |a|
send(a).each do |r|
if r.send(:changed_notably?) || r.marked_for_destruction?
self.updated_at = DateTime.now
return
end
end
end
tracking_has_one_associations.each do |a|
r = send(a)
if r.send(:changed_notably?) || r.marked_for_destruction?
self.updated_at = DateTime.now
return
end
end
end
end
class Version < PaperTrail::Version
def associated_versions
Version.where(transaction_id: transaction_id) if transaction_id
end
end
列添加到polymorphic_type
表格。
versions
并设置如下模型:
class AddPolymorphicTypeToVersions < ActiveRecord::Migration
def change
add_column :versions, :polymorphic_type, :string
end
end
我们现在可以获得包括以下关联的版本:
# app/models/version.rb
class Version < PaperTrail::Version
has_many :associations, class_name: 'PaperTrail::VersionAssociation'
end
# app/models/link.rb
class Link < ActiveRecord::Base
has_paper_trail meta: { polymorphic_type: :linkable_type }
belongs_to :linkable, polymorphic: true
end
# app/models/place.rb
class Place < ActiveRecord::Base
has_paper_trail
has_many :links, as: :linkable
def all_versions
f = '(item_type = "Place" AND item_id = ?) OR ' +
'(foreign_key_name = "place_id" AND foreign_key_id = ?) OR ' +
'(polymorphic_type = "Place" AND foreign_key_id = ?)'
Version.includes(:associations).references(:associations).where(f, id, id, id)
end
end