列出所有paper_trail版本(包括关联)的正确方法是什么?

时间:2015-12-16 07:11:34

标签: ruby-on-rails paper-trail-gem

关于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但是还没有人回答。

3 个答案:

答案 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_idParent匹配的版本{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