Ruby on rails 什么';列出所有纸质版本(包括关联)的正确方法是什么?
关于……的问题 当只有关联更改时,将不会为主模型创建版本记录。那么,列出某个记录的所有版本(包括其关联)的正确方法是什么 我应该质疑这样的问题吗?(缺点是此SQL查询可能很长且性能低下。)Ruby on rails 什么';列出所有纸质版本(包括关联)的正确方法是什么?,ruby-on-rails,paper-trail-gem,Ruby On Rails,Paper Trail Gem,关于……的问题 当只有关联更改时,将不会为主模型创建版本记录。那么,列出某个记录的所有版本(包括其关联)的正确方法是什么 我应该质疑这样的问题吗?(缺点是此SQL查询可能很长且性能低下。) 或者我应该在仅更改关联时创建版本记录吗?我想@DavidHam也试过同样的方法,问过了,但还没有人回答。所以,我找到了一种方法来解决这个问题,但它并不完美,而且它不会在每次关联更改时都创建一个新版本。但是,它确实为您提供了一种按时间顺序检索版本的有效方法,以便您可以查看关联更改前后的版本 首先,我检索给定该模
或者我应该在仅更改关联时创建版本记录吗?我想@DavidHam也试过同样的方法,问过了,但还没有人回答。所以,我找到了一种方法来解决这个问题,但它并不完美,而且它不会在每次关联更改时都创建一个新版本。但是,它确实为您提供了一种按时间顺序检索版本的有效方法,以便您可以查看关联更改前后的版本 首先,我检索给定该模型id的关联版本的所有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数组。因此,这些信息对于活动日志等非常有用。通过这种方式将版本及其关联拼凑起来非常简单:
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
同样,这不是一个完美的答案,因为没有一个新的版本每次都有变化,但它是可管理的。这更多的是一种方法,而不是一个具体的答案,但这里是 在我的例子中,我需要一个版本历史记录,以便任何人在更改
子项时,也会更改“父项”上的标志。但我需要一种方式来显示审计跟踪,该跟踪将显示所有子项的初始值,并在任何人更改子项时显示父项的审计行
简单得多,是这样的:
class Parent < ActiveRecord::Base
has_paper_trail
has_many :children
end
class Child < ActiveRecord::Base
has_paper_trail
belongs_to :parent
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
这应该(应该!没有测试)在父项
上创建时间戳,只要子项
发生更改
然后,要在每个版本的父项
上获取:子项
的状态,您可以搜索子项
的版本,以查找事务id
与父项
匹配的版本
# 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
这在我的情况下奏效了,希望这里的东西对你有用。[更新]
我找到了一个更好的方法。您需要更新事务内部的关联才能使此代码正常工作
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
并设置如下模型:
class Parent < ActiveRecord::Base
has_paper_trail
has_many :children
end
class Child < ActiveRecord::Base
has_paper_trail
belongs_to :parent
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
当您使用touch:true
并同时创建多个子项时,如何防止为父项创建多个版本?最后一个代码块(@parent.versions.each…
)是列出版本?至于您的第一个问题,我从未解决父项的多个版本问题,但是在我的例子中,当一个孩子改变时,总是有一个属性被设置在父对象上,所以我不必担心它,我放弃了touch:true。
但是如果,比如说,你用touch:true
改变了三个:子对象,而不是父对象,在同一次抛出中,你用事务执行版本,它可能只在父级上生成一个版本--我不知道它是否会为每个子级更改生成一个版本,并为它们提供所有相同的事务id
。我认为如果这样做,您无法对版本进行分页,对吗?我不知道,我还没有处理过这个问题。Item是主要模型吗?什么时候项目id为零?是的项目是主模型。因此,item_id为nil的情况应该使用该模型的所有记录。老实说,那部分还没有被使用/测试过,所以如果你愿意的话,我可以不用它来编辑我的代码样本。我尝试了你的代码,但它错过了一些关联版本。我的主要模型是Place,但是它的一些关联“外键名称”不是“位置id”。似乎多态关联使用不同的外键名称。要使用此方法跟踪关联,必须在需要删除关联时软删除它们。
# 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
@place.all_versions.order('created_at DESC')