Rails——一个模型上的两个外键都引用同一个模型



我对ActiveRecord关联还很陌生。我正在绘制一个应用程序,用于跟踪一组用户中谁欠了对方的钱。Expense模型和User模型似乎是自然的选择,我只是不知道如何定义两者之间的关系。例如,我想跟踪每项费用的债权人("所有者")和债务人,但实际上这只是返回到User的两个外键。此外,每个用户都可能有多个费用(作为债权人和债务人)。到目前为止,我对关联的最佳猜测是:

class Expense
    # belongs_to or has_one here?
    # Not sure about class => User syntax:
    # need to alias to foreign keys that reference the same model
    belongs_to :creditor, :class => User 
    belongs_to :debtor, :class => User
class User
    # has_many expenses defines a creditor relationship (user owns expense)
    # how to define debtor relationship? (belongs_to...?)
    has_and_belongs_to_many :expenses

我已经阅读了关于关联的Rails指南,但我仍然对外键和联接表相当着迷。非常感谢您的任何意见!

所以这绝对不是一个适用于多对多关系的has_and_belongs_to_many。您只需要使用几个has_many关系。我认为它最终应该是这样的:

编辑:哎呀,我搞砸了,对不起,让我再试一次:

class Expense
  # make sure expense table has 'creditor_id' and 'debtor_id' rows
  belongs_to :creditor, :class_name => "User", :foreign_key => :creditor_id
  belongs_to :debtor, :class_name => "User", :foreign_key => :debtor_id
class User
  has_many :debts, :class_name => "Expense", :foreign_key => :debtor_id
  has_many :credits, :class_name => "Expense", :foreign_key => :creditor_id

其他答案告诉你需要做什么,但对于像我这样刚接触Rails的人来说,把所有这些东西拼凑在一起可能会有点困惑,所以这里有一个完整的解决方案,包括迁移和模型。

此外,作为补充说明:我更喜欢贷款、贷款人和借款人,而不是费用、债权人和债务人,或债务、债权人和借方。主要是因为支出不明确,债务与债务人过于相似。但并不重要;只要做对你有意义的事情,因为你将维护你的代码。

迁移

class CreateLoans < ActiveRecord::Migration
  create_table :loans do |t|
    def up
      t.references :lender
      t.references :borrower
    end
  end
end

在这里,您指定该表中有两列将被称为:贷款人和:借款人,并包含对另一个表的引用。Rails实际上会为您创建名为"lender_id"one_answers"borrower_id"的列。在我们的例子中,它们将分别引用Users表中的行,但我们在模型中指定了这一点,而不是在迁移中。

型号

class Loan < ActiveRecord::Base
  belongs_to :lender, class_name => 'User'
  belongs_to :borrower, class_name => 'User'
end

在Loan模型上创建一个名为:lender的属性,然后指定该属性与User类相关。Rails看到"belongs_to",将在loans表中查找我们上面定义的名为"lender_id"的列,并使用它来存储外键。那么你也在为借款人做同样的事情。

这将允许您通过贷款模型的实例访问您的贷款人和借款人,这两个实例都是用户模型的实例,如下所示:

@loan.lender # Returns an instance of the User model
@loan.borrower.first_name # Returns a string, as you would expect

顺便说一句:在这种情况下,"belongs_to"命名法很有意义,但在其他地方可能会有点混乱。只要记住,它总是用于任何包含外键的东西。

class User < ActiveRecord::Base
  has_many :loans_as_lender, :class_name => 'Loan', :foreign_key => 'lender_id'
  has_many :loans_as_borrower, :class_name => 'Loan', :foreign_key => 'borrower_id'
end

在这里,您正在用户模型上创建一个名为:loans_as_lender的属性,指定该属性与Loan模型相关,并且Loan模型上将其与该属性相关的外键称为"lender_id"。那么你也在做同样的事情:贷款。

这允许你在用户是贷款人或借款人的情况下获得所有贷款,如下所示:

@address.loans_as_lender
@address.loans_as_borrower

执行其中任一操作都将返回Loan模型的一组实例。

如果您的费用迁移看起来像这样:

create_table :expenses do |t|
  t.integer :creditor_id, :null => false
  t.integer :debtor_id, :null => false
  # other attributes here
end

那么您的Expense模型就足够了。如果你看一下belongs_to的文档,你会发现它会正确地将外键推断到用户表中:

:外国密钥

指定用于关联的外键。默认情况下,这被猜测为带有"_id"后缀的关联的名称。所以定义belong_to:perse关联的类将使用"person_id"作为默认值:foreign_key。类似地,belongs_to:favorite_person,:class_name=>"person"将使用的外键"favorite_person_id"。

因此,您不需要在此处显式指定外键。如果您在支出模型中对id使用其他命名约定,则需要在关联中显式指定它们。

对于用户模型,您与费用没有多对多的关系——费用总是属于一个债务人和一个债权人。因此,您只需要两个has_many关联:

has_many :debts,  :class_name => 'Expense', :foreign_key => :debtor_id
has_many :credits :class_name => 'Expense', :foregin_key => :creditor_id

最新更新