Ruby on rails 什么';列出所有纸质版本(包括关联)的正确方法是什么?

Ruby on rails 什么';列出所有纸质版本(包括关联)的正确方法是什么?,ruby-on-rails,paper-trail-gem,Ruby On Rails,Paper Trail Gem,关于……的问题 当只有关联更改时,将不会为主模型创建版本记录。那么,列出某个记录的所有版本(包括其关联)的正确方法是什么 我应该质疑这样的问题吗?(缺点是此SQL查询可能很长且性能低下。) 或者我应该在仅更改关联时创建版本记录吗?我想@DavidHam也试过同样的方法,问过了,但还没有人回答。所以,我找到了一种方法来解决这个问题,但它并不完美,而且它不会在每次关联更改时都创建一个新版本。但是,它确实为您提供了一种按时间顺序检索版本的有效方法,以便您可以查看关联更改前后的版本 首先,我检索给定该模

关于……的问题

当只有关联更改时,将不会为主模型创建版本记录。那么,列出某个记录的所有版本(包括其关联)的正确方法是什么

我应该质疑这样的问题吗?(缺点是此SQL查询可能很长且性能低下。)


或者我应该在仅更改关联时创建版本记录吗?我想@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')