ruby-on-rails 列出所有PaperTrail版本(包括关联)的最有效方法是什么?

a9wyjsp7  于 2023-04-13  发布在  Ruby
关注(0)|答案(3)|浏览(119)

这个问题与Rails的gem PaperTrail有关。
当只有关联更改时,不会为主模型创建版本记录。因此,列出某个记录(包括其关联)的所有版本的最有效方法是什么?
查询应该如下所示吗?

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 })

上面的SQL查询可能太长,并且性能很低。
或者,当仅更改关联时,是否应创建版本记录?
我认为大卫Ham基于his related question尝试了类似的方法。

wb1gzix0

wb1gzix01#

所以,我找到了一种方法来做到这一点,但它并不完全漂亮,它不会在每次关联更改时创建一个新版本。然而,它确实为您提供了一种有效的方法来按时间顺序检索版本,以便您可以看到关联更改之前/之后的版本。
首先,我检索关联版本的所有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

同样,这不是一个完美的答案,因为每次有变化时都不会有新版本,但它是可管理的。

hgb9j2n6

hgb9j2n62#

这更像是一种方法,而不是一个具体的答案,但这里去。
在我的例子中,我需要一个版本历史记录,以便任何人更改Child时,他们也会更改“Parent”上的标志。但我需要一种方法来显示审计跟踪,该跟踪将显示所有子节点的初始值,以及任何人更改子节点时父节点的审计行。
简单来说,就是这样:

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更改时。
然后,要获得Parent的每个版本中:children的状态,您可以搜索Child的版本,以找到transaction_idParent匹配的版本。

# 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

这在我的情况下工作,希望这里的东西对你有用。

xeufq47z

xeufq47z3#

更新

我发现了一个更好的方法来列出所有包含关联的PaperTrail版本。我更新了事务中的关联以使代码工作。

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

原始答案

下面的代码是迄今为止我发现的最有效的方法。
谢谢John Schaum的回答,帮助了我。
在开始之前,我向“versions”表添加了一个名为polymorphic_type的列。

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

现在我可以使用下面的代码行获取它的版本,包括关联。

@place.all_versions.order('created_at DESC')

相关问题