通过中介在同一模型上建立多对多关系 table

Many-to-many relationship on the same model via an intermediary table

我正在处理可以是购买或退款的交易,我需要将它们相互关联起来。一次购买可以多次退款(部分退款),一次退款可以连接多次购买。

我尝试按照描述建立关系here

这是我的交易模型:

class Transaction < ApplicationRecord
  has_many :refunds_purchases, foreign_key: :transaction_id, class_name: 'RefundsPurchase'
  has_many :refunds, through: :purchases_refunds, source: :refund

  has_many :purchases_refunds, foreign_key: :transaction_id, class_name: 'RefundsPurchase'
  has_many :purchases, through: :refunds_purchases, source: :purchase
end

这是关联模型:

class RefundsPurchase < ApplicationRecord
  belongs_to :purchase, foreign_key: :purchase_id, class_name: 'Transaction'
  belongs_to :refund, foreign_key: :refund_id, class_name: 'Transaction'
end

当我调用 transaction.refunds 时,它失败并返回 NameError: uninitialized constant Transaction::RefundsPurchase",因此它试图在命名空间前加上当前的 class 前缀。如果我将关联模型移动到 Transaction::RefundsPurchase 命名空间,它会为 #Transaction:0x000055633d746440`

提供 NoMethodError: undefined method refunds_purchases'

我做错了什么?

这里真正的问题实际上是您对域的其余部分建模的方式存在缺陷。您缺少包装一组购买的模型:

class Purchase < ApplicationRecord
  belongs_to :purchase_order
  has_many :transactions, through: :purchase_orders
end

class PurchaseOrder < ApplicationRecord
  has_many :purchases
  has_many :transactions
end 

class Transaction < ApplicationRecord
  belongs_to :purchase_order
  has_many :purchases, through: :purchase_order
end

一个更典型的命名方案是订单项订单。如果您想沿着使用单一 Table 继承而不是两个不同的 table 的路线走下去,您可以通过以下方式为退款和付款设置单独的关联:

class Purchase < ApplicationRecord
  belongs_to :purchase_order
  has_many :transactions, through: :purchase_orders
end

class PurchaseOrder < ApplicationRecord
  has_many :purchases
  has_many :transactions
  has_many :payments
  has_many :refunds
end 

# make sure to add varchar column named 'type' to the table 
class Transaction < ApplicationRecord
  belongs_to :purchase_order
  has_many :purchases, through: :purchase_order
end

class Payment < Transaction
end

class Refund < Transaction
end

我真的会考虑使用两个不同的 tables 进行退款和付款,如果您需要将其视为同质集合,则使用联合(或视图)。它将减少数据库与应用程序的关联,您希望获得比退款更多的付款,并且需要查询更多 table。

如果你想退款可以绑定到单次购买(特定订单项 order) 您可以添加一个单独的可为空的外键列:

class Transaction < ApplicationRecord
  belongs_to :purchase_order, optional: true
  belongs_to :purchase, optional: true
  has_many :purchases, through: :purchase_order

  def item
    purchase_order || purchase
  end
end

或者使用多态关联:

class Transaction < ApplicationRecord
  belongs_to :item, polymorphic: true
  has_many :purchases, through: :purchase_order
end

这有一个重要的警告,即它排除了使用外键约束来保证参照完整性,这在涉及金钱时似乎是一个非常糟糕的主意。