我有一段很难模型化的关系。
我有一个Subscription
类,它是一个常规的ActiveRecord模型,它可以有一个或多个PaymentSources
。然而,问题是支付源可以指CreditCard
或BankAccount
。
考虑到这些模型具有非常不同的相关数据,我认为STI在这里不是一个好的选择。所以我想知道,对于Rails中另一个模型的模型has_many实际上是2个或更多不共享相同数据布局的类的抽象,是否有一种既定的或推荐的方法。
理想情况下,在这个特定的示例中,我可以说类似subscription.payment_source.default
的内容,并根据用户选择的首选计费方法来引用CreditCard
或BankAccount
。
发布于 2018-05-31 16:55:00
TLDR:
经过深思熟虑后更新,我将选择选项2(更完整的解决方案),它是面向未来的灵活的,但如果您不需要所有这些复杂性,我将只使用选项1。
选项1:
class Subscription < ApplicationRecord
belongs_to :credit_card
belongs_to :bank_account
def payment_sources
[credit_card, bank_account].compact
end
def default_payment_source
case user.preferred_billing_method # assuming you have an integer column in users table called `preferred_billing_method`
when 0 then credit_card # asssuming 0 means "Credit Card"
when 1 then bank_account # assuming 1 means "Bank Account"
else NotImplementedError
end
end
end
用法
Subscription.first.default_payment_source
# => returns either `CreditCard` or `BankAccount`, or `nil`
Subscription.first.payment_sources.first
# => returns either `CreditCard` or `BankAccount`, or `nil`
选项2:
class User < ApplicationRecord
belongs_to :default_payment_source, class_name: 'PaymentSource'
has_many :subscriptions
end
class Subscription < ApplicationRecord
belongs_to :user
has_many :payment_sources_subscriptions
has_many :payment_sources, through: :payment_sources_subscriptions
end
# This is just a join-model
class PaymentSourcesSubscription < ApplicationRecord
belongs_to :subscription
belongs_to :payment_source
validates :subscription, uniqueness: { scope: :payment_source }
end
# this is your "abstract" model for "payment sources"
class PaymentSource < ApplicationRecord
belongs_to :payment_sourceable, polymorphic: true
has_many :payment_sources_subscriptions
has_many :subscriptions, through: :payment_sources_subscriptions
validates :payment_sourceable, uniqueness: true
end
class CreditCard < ApplicationRecord
has_one :payment_source, as: :payment_sourceable
end
class BankAccount < ApplicationRecord
has_one :payment_source, as: :payment_sourceable
end
用法:
User.first.default_payment_source.payment_sourceable
# => returns either `CreditCard` or `BankAccount`, or `nil`
Subscription.first.payment_sources.first.payment_sourceable
# => returns either `CreditCard` or `BankAccount`, or `nil`
https://stackoverflow.com/questions/50619853
复制相似问题