dsp_099 dsp_099 - 5 months ago 10
Ruby Question

Correct way of making a polymorphic ActiveRecord association

I have a

User
model and a
Dispute
model. A dispute contains a list of involved users, as well as an "accuser" and a "defendant". I want to be able to do this:

Dispute.first.users
#[<User 1>, <User 2>]

Dispute.first.accuser
#<User 1>

Dispute.first.defendant
#<User 2>


So here's my models:

class Dispute < ApplicationRecord
has_and_belongs_to_many :users
belongs_to :accuser, polymorphic: true
belongs_to :defendant, polymorphic: true
end
class User < ApplicationRecord
has_and_belongs_to_many :disputes

has_one :user, as: :accuser
has_one :user, as: :defendant
end


Migrations:

class CreateDisputes < ActiveRecord::Migration[5.0]
def change
create_table :disputes do |t|
t.references :accuser, polymorphic: true, index: true
t.references :defendant, polymorphic: true, index: true
end
end
end
class CreateUsers < ActiveRecord::Migration[5.0]
def change
create_table :users do |t|
t.timestamps
end
end
end


This gives me the behavior I want except that
Dispute.new.save
errors out unless I assign a user to
dispute.accuser
and
dispute.defendant
.

It also looks wrong: Shouldn't a user have one dispute as an accuser/defendant? I can't seem to get it to work though.

Answer

Can I suggest something more simple? I hope I got it right.

Models:

class Dispute < ApplicationRecord
  belongs_to :accuser,    class_name: 'user'
  belongs_to :defendant,  class_name: 'user'

  def users
    [accuser, defendant]
  end 

end

class User < ApplicationRecord
end

Migration:

class CreateDisputes < ActiveRecord::Migration[5.0]
  def change
    create_table :disputes do |t|
      t.integer :accuser_id
      t.integer :defendant_id
    end
  end
end
Comments