Home > OS >  Rails self reference one model with has_many
Rails self reference one model with has_many

Time:09-17

We want to achieve the following:

  1. Be able to compare 'Projects' with other (multiple) Projects.
  2. Save the comparison reference in the database.

In the past we did this by storing an array in the database like below:

t.string "comparisons", default: [], array: true

Now we are thinking about using another table - unless there is a better way?

Ideally something like this:

We already have this table:

|  projects |
|-----------|
| id | name |
| 1  | abc  |
| 2  | def  |
| 3  | ggg  |
| 4  | fff  |

We want to create another table similar to this:

|       project_comparisons     |
|-------------------------------|
| id | project_id | compared_id |
| 1  |     1      |      2      |
| 2  |     1      |      4      |
| 3  |     2      |      3      |
| 4  |     2      |      4      |

Where in the end we could do something like this:

Project.find(1).project_comparisons.each do |x|
  x.name
end

# Output:
'def'
'fff'

But we are getting lost in the relationship setup.

This is what we have so far:

rails g model ProjectComparison project:references compared:references

# Migration file (edited)
create_table :project_comparisons do |t|
  t.references :project, foreign_key: true
  t.references :compared
end


class Project
  has_many :project_comparisons
  has_many :projects, through: :project_comparisons
  # ... Here I think we need something in the line above?
end


class ProjectComparison
  belongs_to :project
end

This now has the the incorrect output. If we now iterate the example:

Project.find(1).project_comparisons.each do |x|
  x.name
end

# Output:
# aaa
# aaa

It should have been 'def' and 'fff'

Can I somehow specify that we want the 'compared_id' to get the correct Project, or are we on the wrong path here?

CodePudding user response:

What about something like:

create_table :project_comparisons do |t|
  t.references :project, foreign_key: true
  t.references :compared, foreign_key: { to_table: 'projects'}
end


class Project
  has_many :project_comparisons
  has_many :compared, through: :project_comparisons
end


class ProjectComparison
  belongs_to :project
  belongs_to :compared, class_name: "Project", foreign_key: "compared_id"
end

Project.find(1).compared.each do |x|
  x.name
end
  • Related