Rails - два внешних ключа на одной модели относятся к одной модели

Я новичок в ассоциациях 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 об ассоциациях, но я все еще довольно потерял внешние ключи и присоединил таблицы. Любой вход очень ценится!

Ответ 1

Таким образом, это не означает has_and_belongs_to_many для отношений "многие ко многим". Вам просто нужно использовать пару отношений has_many. Я думаю, это должно выглядеть так:

Изменить: oops Я помыл, что немного извините, позвольте мне еще раз пойти:

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

Ответ 2

Другие ответы говорят вам, что вам нужно делать, но это может быть немного запутанным для людей, которые являются новыми для Rails, как и я, для того, чтобы объединить все эти вещи вместе, так что это полное решение, включая как Migrations и модели.

Кроме того, в качестве дополнительной заметки: я предпочитаю Кредиты, Кредитор и Заемщик для покрытия расходов, Кредитор и Должник, или Долг, Кредитор и Должник. В основном потому, что Расходы неоднозначны, а задолженность слишком похожа на Debtor. Но это не так важно; просто делайте то, что имеет смысл для вас, поскольку вы будете поддерживать свой код.

Миграция

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

Здесь вы указываете, что в этой таблице есть два столбца, которые будут называться: lender и: заемщик и которые содержат ссылки на другую таблицу. Rails фактически создадут для вас столбцы, называемые "lender_id" и "loaner_id". В нашем случае каждая строка ссылок будет указана в таблице "Пользователи", но мы укажем, что в моделях, а не в миграциях.

Модели

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

Здесь вы создаете свойство в модели Loan с именем: lender, а затем указываете, что это свойство связано с классом User. Рельсы, видя "принадлежность", будут искать столбец в таблице ссуд под названием "lender_id" , который мы определили выше, и используем его для хранения внешнего ключа. Тогда вы делаете то же самое для заемщика.

Это позволит вам получить доступ к вашему кредитору и заемщику, оба экземпляра модели пользователя, через экземпляр модели займа, например:

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

В качестве побочного примечания: номенклатура "принадлежит к" делает приличный смысл в этом случае, но может быть путаницей в другом месте. Просто помните, что он всегда используется в зависимости от того, что содержит внешний ключ.

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, который связывает его с этим свойством, называется "lender_id" . Затем вы делаете то же самое для: loans_as_borrower.

Это позволяет получить все кредиты, в которых Пользователь является кредитором или заемщиком, например:

@address.loans_as_lender
@address.loans_as_borrower

Выполнение любого из них вернет массив экземпляров модели Loan.

Ответ 3

Если миграция расходов выглядит так:

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

то достаточно вашей модели Expense. Если вы посмотрите на документацию для belongs_to, вы увидите, что она правильно выведет внешние ключи в таблицу пользователя:

: foreign_key

Укажите внешний ключ, используемый для ассоциации. По умолчанию предполагается, что это имя ассоциации с суффиксом "_id". Так класс, который определяет ассоциацию belongs_to: person, будет использовать "person_id" по умолчанию: foreign_key. Аналогично, принадлежит_to: favorite_person,: class_name = > "Person" будет использовать внешний ключ "Favorite_person_id".

Поэтому вам не нужно явно указывать здесь внешний ключ. Если вы используете другие правила именования для идентификаторов в вашей модели затрат, вам необходимо явно указать их в своих ассоциациях.

Для вашей модели пользователя у вас нет отношения many_to_many с расходами - расход всегда принадлежит ровно одному должнику и ровно одному кредитору. Итак, все, что вам нужно, это две ассоциации has_many:

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