stewart715
stewart715

Reputation: 5647

many_to_many associations in rails

This has been a nightmare for me and I'm willing to give an arm and an eyeball to the person who can help me :P I'm going to explain this as best as possible.

When a user edits their 'profile' they can type in skills that they have in nested attributes fields (they can click 'Add Another Skill' and it will populate another field for them to type). When they begin to type, jquery-autocomplete kicks in. They MUST select an item from autocomplete. When they save, for example, 3 skills, we get this:

table: skills

+-----------+----------------+
|  user_id  |     skill      |
+-----------+----------------+
|     4     |  Photoshop     |
+-----------+----------------+
|     4     |  Illustrator   |
+-----------+----------------+
|     4     |  InDesign      |
+-----------+----------------+

Now, what I want to do is NOT store the actual skill names. Instead, I want to store the ID of the skill from skills_vocabulary list (which is what the jquery-autocomplete uses).

table: skills_vocabulary

+------+----------------+
|  id  |     skill      |
+------+----------------+
|  1   |  Illustrator   |
+------+----------------+
|  2   |  Dreamweaver   |
+------+----------------+
|  3   |  After Effects |
+------+----------------+
|  4   |  InDesign      |
+------+----------------+
|  5   |  Photoshop     |
+------+----------------+
|  6   |  Flash         |
+------+----------------+

THUS, the skills table should look like this:

+-----------+----------------+
|  user_id  |    skill_id    |
+-----------+----------------+
|     4     |        5       |
+-----------+----------------+
|     4     |        1       |
+-----------+----------------+
|     4     |        4       |
+-----------+----------------+

Any help would be greatly, greatly, greatly appreciated. I've been at war with this for an embarrassing 3 months.

Thank you all.

EDIT/

<%= f.fields_for :skills do |builder| %>
    <%= render "skills_fields", :f => builder %>
  <% end %>
  <p><%= link_to_add_fields "Add a Skill", f, :skills %></p>     

_skills_fields.html.erb

<div class="fields ac skills">
    <div class="clearfix">
        <span class="left"><%=f.autocomplete_field :name, users_autocomplete_skills_vocab_name_path, :class => 'restricted_to_autocomplete' %></span><span class="remove_link left"><%= link_to_remove_fields "[x]", f %></span>
    </div>
</div>

EDIT 2/

Right now it's just

skill.rb

class Skill < ActiveRecord::Base
  belongs_to :user
end

user.rb

class User < ActiveRecord::Base
  has_many :tskills, :dependent => :destroy
  accepts_nested_attributes_for :tskills, :allow_destroy => true, :reject_if => lambda { |a| a[:name].blank? }
end

skills_vocabulary.rb

class SkillsVocabulary < ActiveRecord::Base

end

My controller code is the basic index, show, new, create, update, destroy

The list of skills (about 10,000 items long) is in the table skills_vocabularies

Upvotes: 0

Views: 217

Answers (1)

Fabio
Fabio

Reputation: 19176

This should be your classes:

class User < ActiveRecord::Base
  has_and_belongs_to_many :skills
end

class Skill < ActiveRecord::Base
  has_and_belongs_to_many :users
end

and this is the migration which creates them

class CreateUsers < ActiveRecord::Migration
  def self.up
    create_table :users do |t|
      t.string :name
      t.timestamps
    end
  end

  def self.down
    drop_table :users
  end
end

class CreateSkills < ActiveRecord::Migration
  def self.up
    create_table :skills do |t|
      t.string :name
      t.timestamps
    end
  end

  def self.down
    drop_table :skills
  end
end

class SkillsUsers < ActiveRecord::Migration
  def self.up
    create_table :skills_users, :id => false do |t|
      t.integer :skill_id
      t.integer :user_id
    end
  end

  def self.down
    drop_table :skills_users
  end
end

then add a new skill is as simple as

user.skills << Skill.find(1) # to add a skill
user.skills = Skill.all      # to add many of them

Look here fo further details

Update: I ve tried the autocomplete gem and I think you should accept the Rubish Gupta suggestion, i.e. use my models plus jquery token input because it will handle pretty well many-to-many associations. Autocomplete gem as far as I saw is for 1-to-n associations.

Moreover you have a full working example in rails to play with.

Upvotes: 3

Related Questions