Search code examples
ruby-on-railsrubyactiverecordruby-on-rails-5polymorphic-associations

ActiveRecord associations with two different models


I'm having a tough time wrapping my head around how I should be configuring my tables + associations.

I have a Lawsuit model. A lawsuit has_many parties (defendants, plaintiffs, attorneys, etc.). A party, in turn, can either be a Person or a Company. Ultimately, I want to be able to get:

  • A person’s lawsuits (@person.lawsuits);
  • A company’s lawsuits (@company.lawsuits); and
  • A lawsuit’s parties (@lawsuit.parties), which can be either people or companies.

This is how I have my tables + models set up currently:

people

| id | fname  | lname | date_of_birth |
| -- | ------ | ----- | ------------- |
|  1 | John   | Smith |    1974-02-04 |
|  2 | George | Glass |    1963-07-29 |

companies

| id | name      | duns      | ticker | address      |
| -- | --------- | --------- | ------ | ------------ |
|  1 | Acme Inc. | 239423243 | ACME   | 123 Main St. |

lawsuits

| id | jurisdiction | court | case_no    | title                       |
| -- | ------------ | ----- | ---------- | --------------------------- |
|  1 |      federal | SDNY  | 18-CV-1234 | Smith v. Glass, Acme, et al |

lawsuit_parties

| id | lawsuit_id | person_id | company_id | role      |
| -- | ---------- | --------- | ---------- | --------- |
|  1 |          1 |         1 |            | plaintiff |
|  2 |          1 |         2 |            | defendant |
|  3 |          1 |           |          1 | defendant |
# models/lawsuit.rb:
class Lawsuit < ApplicationRecord
    has_many :lawsuit_parties

    def parties
        self.lawsuit_parties
    end

    def defendants
        self.parties(where(lawsuit_parties: {role: 'defendant'})
    end

    def plaintiffs
        self.parties(where(lawsuit_parties: {role: 'plaintiff'})
    end

    def attorneys
        self.parties(where(lawsuit_parties: {role: 'attorney'})
    end
end
# models/lawsuit_party.rb
class LawsuitParty < ApplicationRecord
    belongs_to :person
    belongs_to :company
end
# models/person.rb
class Person < ApplicationRecord
    has_many :lawsuit_parties
    has_many :lawsuits, through: :lawsuit_parties
end
# models/company.rb
class Company < ApplicationRecord
    has_many :lawsuit_parties
    has_many :lawsuits, through: :lawsuit_parties
end

Any help you would be much appreciated…


Solution

  • You're on the right track, but you'll need to introduce a polymorphic relationship onto your Join Model to get this type of modeling to work. An Enum can handle differentiating between Defendants and Plaintiffs, as well as provide several scopes/methods you're asking for for free.

    class LawsuitParty < ApplicationRecord
        belongs_to :lawsuit
        belongs_to :partiable, polymorphic: true
    
        enum role: [:defendant, :plaintiff]
    end
    

    You'll need to write a migration to change your lawsuit_parties table to the following columns (all Rails convention names):

    partiable_id   = Integer
    partiable_type = String
    role           = String
    
    lawsuit_parties
    
    | id | lawsuit_id | partiable_id | partiable_type | role      | 
    | -- | ---------- | ------------ | -------------- | ----------|
    |  1 |          1 |            1 | Person         | defendant |
    |  2 |          1 |            2 | Company        | plaintiff |
    |  3 |          1 |            1 | Company        | defendant |
    

    Next, tell Rails that Person and Company records are associated with many Lawsuit's using has_many's :as option.

    class Person < ApplicationRecord
        has_many :lawsuit_parties, as: :partiable
        has_many :lawsuits, through: :lawsuit_parties
    end
    

    Add the same has_many :lawsuit_parties, as: :partiable to Company, or any other models that may come later (i.e. Judge or JuryMember).

    Once you have a LawsuitParty setup like this, you should be all set.