简体   繁体   中英

How can I use two records from the same Rails model as foreign keys in a different Rails model?

I have two models: person.rb and relationship.rb

I need my :relationships table to reference two rows from the :people table as foreign keys.

Here are the migrations for both tables:

class CreatePeople < ActiveRecord::Migration[5.1]
  def change
    create_table :people do |t|
      t.string :first_name
      t.string :second_name
      t.integer :age
      t.string :gender
      t.timestamps
    end
  end
end

class CreateRelationships < ActiveRecord::Migration[5.1]
  def change
    create_table :relationships do |t|
      t.references :person_a
      t.references :person_b
      t.string :status
      t.timestamps
    end
  end
end

The idea is the :person_a and :person_b fields will both be individual records from the :people table referenced as foreign keys, while the :status field will just be a description of their relationship ("Married", "Friends", "Separated", etc.)

I'm trying to find out:

1) What is the additional code I have to write in the CreateRelationships migration above in order to set :person_a and :person_b up as foreign keys from the :people table?

2) What code do I need to write in the model files (person.rb and relationship.rb) for both tables below to define the relationship structure I'm talking about?

class Person < ApplicationRecord
end

class Relationship < ApplicationRecord
end

I've found one other question on here that deals with this issue, but the answers given were conflicting, some incomplete, and others working with older versions of Rails. None of them have worked for me.

I'm using Rails 5.1.4

You have defined you migration correctly just add the following in your model to define the relationship between the model.

    class Person < ApplicationRecord::Base
      has_many :relationships, dependent: :destroy
    end

    class Relationship < ApplicationRecord::Base
      belongs_to :person_a, :class_name => 'Person'
      belongs_to :person_b, :class_name => 'Person'
    end

This allows you to access the Person that a Relationship belongs to like this:

@relationship.person_a #user assigned as the first person.
@relationship.person_b #user assigned as the second person.

Hope this works.

I had to do the same for a chat module, this is an example to how you can do it:

class Conversations < ApplicationRecord
  belongs_to :sender, foreign_key: :sender_id, class_name: 'User'
  belongs_to :recipient, foreign_key: :recipient_id, class_name: 'User'
end

class User < ApplicationRecord
  ...
  has_many :conversations
  has_many :senders, through: :conversations, dependent: :destroy
  has_many :recipients, through: :conversations, dependent: :destroy
  ...
end

More explanations at complex-has-many-through

Hope it helps,

EDIT: Apologies for a rushed and wrong answer.

Initially, I thought that simple has_many/belongs_to association is possible and sufficient.

class Person < ApplicationRecord
  has_many :relationships #dependent: :destroy
end

class Relationship < ApplicationRecord
  belongs_to :person_a, class_name: "Person"
  belongs_to :person_b, class_name: "Person"

  enum status: [:married, :friends, :separated]
end

As @engineersmnky pointed out, has_many association can't work here because there is no person_id column in relationships table. Since we can declare only one custom foreign key in has_many association, it's not possible to declare it here this way. belongs_to will work, but I don't think that's enough.

One way is to skip declaring has_many and stick to custom method for querying relationships:

class Person < ApplicationRecord
  def relationships
    Relationship.where("person_a_id = ? OR person_b_id = ?", id, id)
  end
end

It will give you an ActiveRecord::Relation to work with, containing exactly the records you need. The drawbacks of this solution are numerous - depending on your needs, you will probably need more code for inserting data, starting with a setter method to assign relationships to people...

What could be a real solution, is to have a composite primary key in Relationship model - composed of :person_a_id and :person_b_id . ActiveRecord doesn't support composite primary keys, but this gem seems to fill the gap. Apparently it allows to declare such key and use it as a foreign key in a has_many association. The catch is that your person_a/person_b pairs would have to be unique across relationships table.

You can do like this In relationship model write

belongs_to : xyz, class_name: "Person", foreign_key: "person_a"
belongs_to : abc, class_name: "Person", foreign_key: "person_b"

In Person model write

has_many :relationships, dependent: :destroy

hope it will help

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM