Rails - 一个模型上的两个外键都指向相同的模型

时间:2012-02-07 08:58:41

标签: sql ruby-on-rails activerecord

我是ActiveRecord协会的新手。我正在草拟一个应用程序,该应用程序可以跟踪在一组用户中欠谁的钱。 Expense模型和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指南,但我仍然在外键和连接表上相当丢失。任何意见都非常感谢!

3 个答案:

答案 0 :(得分:23)

所以这肯定不是多对多关系的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

答案 1 :(得分:18)

其他答案告诉你你需要做什么,但是对于那些刚接触Rails的人来说,这可能会让我感到困惑,因为我将所有这些东西拼凑在一起,所以这里有一个完整的解决方案,包括两个迁移和模特。

另外,作为旁注:我更倾向于贷款,贷款人和借款人,费用,债权人和债务人,或债务,债权人和债务人。主要是因为费用含糊不清,债务与债务人太相似。但 并不重要;只是做对你有意义的事情,因为你将维护你的代码。

迁移

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

此处指定此表中有两列将被称为:lender和:borrower,它们包含对另一个表的引用。 Rails实际上会为你创建名为'lender_id'和'borrower_id'的列。在我们的例子中,他们将每个引用Users表中的行,但我们在模型中指定,而不是在迁移中。

模型

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

在这里,您要在Loan模型上创建一个名为:lender的属性,然后指定此属性与User类相关。看到'belongs_to'的Rails将在loan表中查找一个名为'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

在这里,您要在User模型上创建一个名为:loans_as_lender的属性,指定此属性与Loan模型相关,并且Loan模型上与此属性相关的外键称为“lender_id”。然后你正在做同样的事情:loans_as_borrower。

这使您可以获得用户是贷款人或借款人的所有贷款,如下所示:

@address.loans_as_lender
@address.loans_as_borrower

执行其中任何一项操作都会返回Loan模型的实例数组。

答案 2 :(得分:1)

如果费用迁移如下:

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

然后您的费用模型就足够了。如果您查看documentation for belongs_to,您会看到它会正确地将外键推断到用户表中:

  

:foreign_key

     

指定用于关联的外键。默认情况下,这被认为是具有“_id”后缀的关联的名称。所以   定义belongs_to:person关联的类将使用   “person_id”作为默认值:foreign_key。同样,belongs_to   :favorite_person,:class_name =&gt; “人”将使用外键   “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