【问题标题】:Rails - has_one association to one of two possible modelsRails - has_one 与两种可能模型之一的关联
【发布时间】:2016-03-23 08:00:28
【问题描述】:

关于 Rails 关联的问题,其中 has_one 关联是与两种可能模型之一的关联。类似于多态关联,但反过来。

所以我有两种不同的银行账户,都属于UserConfig

class SwedishBankAccount
  belongs_to :user_config
end

class ForeignBankAccount
  belongs_to :user_config
end

问题是如何在我的UserConfig 中进行管理。它应该与SwedishBankAccountForeignBankAccount 有一个has_one 关联,但不能同时与两者关联。

【问题讨论】:

  • 为什么这里没有使用polymorphic关联?
  • 你介意举个例子来说明我是怎么做到的吗?也许我只是瞎了,但我不明白怎么做:)

标签: ruby-on-rails


【解决方案1】:

你可以使用多态关系。在UserConfig 模型中添加target_idtarget_type

class UserConfig < ActiveRecord::Base
  belongs_to :target, :polymorphic => true
end

那你就用SwedishBankAccount作为模型

class SwedishBankAccount < ActiveRecord::Base
  has_one :user_config, :as => :target
end

在模型ForeignBankAccount As

class ForeignBankAccount < ActiveRecord::Base
  has_one :user_config, :as => :target
end

【讨论】:

  • UserConfig belongs_to 银行账户感觉很奇怪,但如果我是对的,那只能决定foreign_key 的位置。
  • 实际上你的账户外键存储在UserConfig 模型和type
  • 我在考虑关联名称而不是 foreign_key 的位置。 UserConfig 可以在没有任何类型的银行帐户关联的情况下存在,但在没有关联的UserConfig 的情况下,SwedishBankAccount 不应该存在。 UserConfig 拥有银行账户,而不是相反。我认为如果将has_one 转移到银行帐户,那将变得有些不清楚。但这只是我的意见:)
  • 我改变了主意。这种方法是我发现的最好的。虽然我仍然认为配置属于银行账户很奇怪,但它保证只有一个银行账户连接到模型。在下面我自己的回答中,UserConfig 实际上可以同时拥有两个银行账户(这应该是不可能的)。
【解决方案2】:

您可以为此模型创建抽象类BankAccount

class BankAccount < ActiveRecord::Base
  self.abstract_class = true
  belongs_to :user_config
end

class SwedishBankAccount < BankAccount
end

class ForeignBankAccount < BankAccount
end

class UserConfig < ActiveRecord::Base
  has_one :bank_account
end

这里没有BankAccount 的表格,但您可以将模型的公共部分放在那里。

【讨论】:

  • 不知道您可以与抽象类建立关联。我会测试它! :)
  • 我更喜欢这个解决方案。很明显UserConfigBankAccount 的所有者,而且正如你所说,公共部分可以放在抽象类中。
  • 我在您的方法中遇到了一些问题。如果我尝试使用user_config.bank_account 访问用户的银行账户,我会得到ActiveRecord::StatementInvalid: Could not find table ''。抽象类显然缺少表名。关于如何解决这个问题的任何想法?
  • @ehannes 你说得对,我找不到任何解决方案。可能您应该使用 Vishal JAIN 答案,或者问另一个问题。
  • 我在使用这种技术时遇到了几个问题。 build_association(attributes = {})create_association(attributes = {})create_association!(attributes = {}) 等方法也不起作用。
【解决方案3】:

不幸的是,正如 zishe 所建议的那样,与抽象类建立关联并不是一个好主意(请参阅该答案的 cmets)。 Vishal JAIN 的回答会奏效,但我认为由于所有权的变化,这会使正确了解架构变得更加困难。

我最终与这两种类型的银行账户都有关联。一个丑陋的解决方案,但这是我能想到的最好的解决方案。如果其他人有更好的想法,请分享您的想法! :)

class UserConfig < ActiveRecord::Base
  has_one :swedish_bank_account
  has_one :foreign_bank_account

  enum bank_account_type [:swedish_bank_account, foreign_bank_account]

  # Returns bank account of type determined by bank_account_type.
  # Returns nil if no account is associated.
  def bank_account
    return swedish_bank_account if swedish_bank_account?
    return foreign_bank_account if foreign_bank_account?
  end
end

【讨论】:

  • 这种方法容易出错。 UserConfig 实际上可以同时拥有两个银行账户。如果有人直接调用swedish_bank_accountforeign_bank_account,而不使用bank_account方法,则返回的银行帐户有50%的可能性为零或不正确。
【解决方案4】:

您可以创建一个名为 account 的新模型,其中 user_id 是主键“无重复”,account_id 指向他在 ForeignBankAccount 或 SweditshBankAccount 中的记录。

这样可以确保没有用户一次拥有超过 1 个帐户。

【讨论】:

  • 感谢您的回答。在我的例子中,User 有一个UserConfig 和一个Profile。然后UserConfig 应该与银行帐户关联。引入一个新的Account 类是不合适的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-03-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多