mysql - 将所有记录关系移动到另一条记录

标签 mysql ruby-on-rails ruby ruby-on-rails-4

我正在尝试将一条记录“合并”到另一条记录及其所有关系子项中。

例如:

我有 vendor1vendor2,它们都有许多包含其他 has_many 的关系。例如,一个供应商有很多 purchase_orders,一个采购订单有很多 ordered_items,一个 ordered_item 有很多 received_items。

如果我将 vendor2 的名称更改为与 vendor1 的名称相同,那么我想销毁 vendor2 但移动所有它的 has_many 到 vendor1

这是我一直在尝试做的事情:

  def vendor_merge(main_vendor, merge_vendor)
    relationships = [
      merge_vendor.returns, merge_vendor.receiving_and_bills,
      merge_vendor.bills, merge_vendor.purchase_orders, merge_vendor.taxes,
      Check.where(payee_id: merge_vendor.id, payee_type: "Vendor"),
      JournalEntryAccount.where(payee_id: merge_vendor.id)
    ]

    relationships.each do |relationship|
      class_name = relationship.class.name
      relationship.each do |r|
        if class_name === "Check"
          r.update(payee_id: main_vendor.id)
        else
          r.update(vendor_id: main_vendor.id)
        end
        r.save
      end

      relationship.delete_all
    end

    merge_vendor.destroy
  end

这样做会给我带来约束错误,因为 has_many 的 has_many 和 has_many through: :ect...

对此有什么直接的解决方案吗?

最佳答案

您需要在您的应用中定义一个合并逻辑。这可能是一个 PORO(普通旧 ruby​​ 对象),如 VendorMerger,它包含所有逻辑以便将 Vendor 记录合并到另一个记录(这也可以在Vendor 模型,但它会污染您的模型)。

这是一个 PORO 的例子:

# lib/vendor_merger.rb
class VendorMerger
  def initialize(vendor_from, vendor_to)
    @vendor_from = vendor_from
    @vendor_to = vendor_to
  end

  def perform!
    validate_before_merge!
    ActiveRecord::Base.connection.transaction do # will rollback if an error is raised in this block
      migrate_related_records!
      destroy_after_merge!
    end
  end

  private

  def validate_before_merge!
    raise ArgumentError, 'Trying to merge the same record' if @vendor_from == @vendor_to
    raise ArgumentError, 'A vendor is not persisted' if @vendor_from.new_record? || @vendor_to.new_record?
    # ...
  end

  def migrate_related_records!
    # see my thought (1) below
    @vendor_from.purchases.each do |purchase|
      purchase.vendor = @vendor_to
      # ...
      purchase.save!
    end
  end

  def destroy_after_merge!
    @vendor_from.reload.destroy!
  end

用法:

VendorMerger.new(Vendor.first, Vendor.last).perform!

此 PORO 允许您将与合并相关的所有逻辑包含到一个文件中。它遵循 SRP(单一责任原则)并使测试和维护变得非常容易(例如:包括一个记录器、自定义错误对象等)。

想法 (1):您可以手动检索要合并的数据(如我的示例所示),但这意味着如果有一天您添加另一个与 Vendor 的关系 模型,假设 Vendor has_many :customers 但忘记将其添加到 VendorMerger,那么它将“静默失败”,因为 VendorMerger 不知道新的关系​​>:客户。要解决此问题,您可以动态获取所有引用了 Vendor 的模型(其中列为 vendor_idclass_name 选项等于 'Vendor' 或关系是多态的并且 XX_type 列包含一个 'Vendor' 值)并将所有外来者从旧 ID 转换为新 ID .

关于mysql - 将所有记录关系移动到另一条记录,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45963983/

相关文章:

ruby-on-rails - 替代 heroku 上简单 cron 任务的延迟作业?

ruby-on-rails - 如何允许用户从 Rails 中的多选框中选择零个选项?

ruby - 使用 Passenger 部署多个 Rail 3.1 应用程序的 'official' 方法

ruby - 如何指定 gem 仅是 JRuby 平台?

php - 如何为数据库中插入的每一行生成一个 15(字母数字)字符长度的唯一字符串?

mysql - MySQL 中的多个连接

ruby-on-rails - 如何在 Rails Engine 中获取 Devise 的 current_user 方法

php - MySQL 控制台中的查询结果与 PHP 的 mysql_query() 的结果不匹配

php - 重复 key 更新 - 不更新

ruby-on-rails - Ruby on Rails - 在另一个模型的表单上添加模型的字段