TLK
TLK

Reputation: 1770

In RoR, how do I create TWO one to one relationship between two tables?

In RoR3,

I have Users and Skills and each skill is created by a user. I wanted to record that, so I created a one to many relationship.

class User < ActiveRecord::Base
  has_many :skills
end

class Skill < ActiveRecord::Base
  belongs_to :user
end

However, each user also has many skills in the sense that, user "Bob" created skill "Kung Fu", user "Charlie" created skill "Karate" and user "Bob" both created and is able to do both "Kung Fu" and "Karate"

How should I represent this with ActiveRecord? Should I just create a new table "user_skills" which has_many :skills? and belong_to :user?

Upvotes: 1

Views: 306

Answers (3)

Daniel
Daniel

Reputation: 3027

Having those two models

class User < ActiveRecord::Base 
   has_and_belongs_to_many :skills
 end

 class Skill < ActiveRecord::Base  
   has_and_belongs_to_many :users
 end

You would have to create an extra migration (without the model)

 rails generate migration CreateSkillsUsersJoin 

which will give you

     class CreateSkillsUsersJoin < ActiveRecord::Migration
         def self.up
            create_table :skills_users, id => false do |t|
                t.references "user"
                t.references "skill"
             end
             add_index :skills_users,["user_id","skill_id"]
         end
         def self.down
            drop_table :skills_users
         end
      end

The methods self.up and self.down you will have yo add them

Upvotes: 1

edgerunner
edgerunner

Reputation: 14973

There are two different associations here. The first is a one-to-many association. An user can be the creator of any number of skills. The second one is a many-to-many association, an user can have many skills and a skill can have many users.

The first one is a simple belongs_to <-> has_many declaration. For the second one, you either need a has_and_belongs_to_many declaration in both models, and a related join table, or a dedicated join model, and a has_many :through declaration. Let's try the first one:

Method 1: HABTM

class User < ActiveRecord::Base
  has_many :created_skills, :class_name => 'Skill', :inverse_of => :creator
  has_and_belongs_to_many :skills
end

class Skill < ActiveRecord::Base
  belongs_to :creator, :class_name => 'User', :inverse_of => :created_skills
  has_and_belongs_to_many :users
end

This requires a join table called "skills_users" that has columns named user_id and skill_id

Method 2: Has many through (Join model)

The second one is similar, but adds a model that acts as the middleman. This has an added benefit that you can include additional columns in the join model, like for example a skill level.

class User < ActiveRecord::Base
  has_many :created_skills, :class_name => 'Skill', :inverse_of => :creator
  has_many :user_skills
  has_many :skills, :through => :user_skills
end

class Skill < ActiveRecord::Base
  belongs_to :creator, :class_name => 'User', :inverse_of => :created_skills
  has_many :user_skills
  has_many :users, :through => :user_skills
end

class UserSkill < ActiveRecord::Base
  belongs_to :user
  belongs_to :skill
end

Upvotes: 4

pjammer
pjammer

Reputation: 9577

You'd be well served using a gem like acts_as_taggable_on which you'd be able to simply setup and use in your User model, something like:

acts_as_taggable_on :skills

Honestly, they've figured all this stuff out, as it's not as simple as what you're trying to do, OR I should rephrase that and say, what you are trying to do is overtly 'complex' and this gem allows you to just keep on, keeping on after it's set up.

Read the Readme.

Upvotes: -1

Related Questions