rrivermcd
rrivermcd

Reputation: 45

Rails 4 has_many through many

I'm stuck on this:

class Worker < ActiveRecord::Base
  has_many :skills
  has_many :jobs, through: :skills
..
end

class Skill < ActiveRecord::Base
  belongs_to :worker
  has_many :jobs
..
end

class Job < ActiveRecord::Base
  has_many :skills
  has_many :workers, through: :skills
..
end

What I'm trying to do is set up a many to many between Skill and Job inside of the `has_many' through relationship?

My question has three parts

  1. Is this possible - using the has_many jobs rather than belongs_to jobs.
  2. If it can be done and the code is wrong, how do I fix it?
  3. How can I create Worker, Skill and Job records? (looking for syntax)

This is a picture (of sorts) of what I'm trying to do, hope it helps... :(

enter image description here

Upvotes: 2

Views: 2689

Answers (1)

Dane O&#39;Connor
Dane O&#39;Connor

Reputation: 77278

You're not giving active record enough information about your relationships. Every :has_many should have a corresponding :belongs_to so that active record knows which table holds the foreign key for each association. Notice that you only have one :belongs_to for three relationships. That smells.

As for fixing the problem, you have at least 2 options:

  • add :has_and_belongs_to_many associations
  • use explicit join tables

My preference is for the latter option. Being forced to name join tables often clarifies the nature of a relationship. On the flip side, I've found that :has_and_belongs_to_many is often too implicit and ends up making my designs more obscure.

Explicit join tables

You might setup your relationships like this (untested):

class Assignment < ActiveRecord::Base
  belongs_to :worker
  belongs_to :job
end

class Qualification < ActiveRecord::Base
  belongs_to :worker
  belongs_to :skill
end

class Worker < ActiveRecord::Base
  has_many :qualifications
  has_many :skills, through: :qualifications

  has_many :assignments
  has_many :jobs, through: :assignments
  ..
end

class Skill < ActiveRecord::Base
  has_many :qualifications
  has_many :workers, through: :qualifications
  has_many :jobs
  ..
end

class Job < ActiveRecord::Base
  has_many :skills
  has_many :workers, through: :assignments
  ..
end

By making the relationships more explicit I think the model is clearer. It should be easier to troubleshoot from here.

EDIT:

If you need to do a traversal like Job.find(1).qualified_workers try making the following adjustment to the above model:

class Job
  has_many :required_competencies
  has_many :skills, through: :required_competencies
  has_many :qualifications, through: :skills
  has_many :qualified_workers, through: qualifications, class_name: :workers
end

class RequiredCompetency
  belongs_to :job
  belongs_to :skill
end

This is explicit about each traversal and names it. If you find these paths through your system are getting really long, I'd consider that a smell. There might be a more direct way to fetch your data or perhaps a better way to model it.

Upvotes: 2

Related Questions