When creating a migration for a model like Contract below, we have to add a foreign key to both the supplier_id and the customer_id on the contracts table. Both of these foreign keys should point to the organisations table - defined by the Organisation model.

erDiagram  
  Contract {  
   int supplier_id FK "organisation.id"  
   int customer_id FK "organisation.id"  
  }  
    
  Organisation ||--o{ Contract : "as supplier"  
  Organisation ||--o{ Contract : "as customer"  

Rails can handle standard migrations for references without modification e.g.

#...
	t.references :organisation, null: false, foreign_key: true
#...

In our scenario, a little modification is required and documentation for this is thin - [references].

For a start we not using inferable reference names i.e. we don’t have a suppliers or customers table but instead we want to use the organisations table.

Migrations

Here we will have to help Rails locate the relevant table - in this case, the organisations table.

The documentation does not give us any clues on how to define the table that the foreign key references, however, tucked away in the codebase we found the to_table option on the foreign key.

With this option, we can define

#...
	t.references :supplier, null: false, foreign_key: {to_table: :organisations}
	t.references :customer, null: false, foreign_key: {to_table: :organisations}
#...

Modelling

On the model side of things, it gets a little confusing too.

To start with the Contract model needs relationships that specify the class_name.

class Contract < ApplicationRecord
	belongs_to :supplier, class_name: "Organisation"  
	belongs_to :customer, class_name: "Organisation"
end

On the other side, we have the Organisation model. The Organisation model has two main relationships has_many :customers and has_many :suppliers, both of which should return a collection of Organisation records.

To have both of these relationships will require a through relationship and this is where naming can become difficult1. The table below describes the relationships on the Organisation model.

name description
contracts_as_customer Collection of Contract records where the Organisation is the customer
contracts_as_supplier Collection of Contract records where the Organisation is the supplier
customers Collection of Organisation records that are Customers of the Organisation
suppliers Collection of Organisations records that are Suppliers to the Organisation

The final Organisation model may look like this

class Organisation < ApplicationRecord  
	has_many :contracts_as_customer, class_name: "Contract", foreign_key: :customer_id  
	has_many :contracts_as_supplier, class_name: "Contract", foreign_key: :supplier_id  
	has_many :customers, through: :contracts_as_supplier, source: :customer  
	has_many :suppliers, through: :contracts_as_customer, source: :supplier
end  
  1. The first iteration of this post revolved around People and Follows in a social network type scenario. Thankfully Organisations and Contracts are easier to follow.Β