0

I'm trying to create a simple many-to-many association for my Artist model. I've tried following the example here but for some reason its just not translating for my particular project. I basically just want users to belong to groups and reflect their memberships in my show view.

<!-- Shows the list of groups an artist belongs to -->

<% if @artist.groups.any? %>
  <%= @artist.groups.each do |group| %>
    <%= group.name %>
  <% end %>
<% end %>

<!-- Shows the members of the groups -->

<% if @artist.members.any? %>
    <p>
      <b>Members:</b>
      <% @artist.members.each do |member| %>
        <%= member.name %>
      <% end %>
    </p>
<% end %>

I currently have my User model setup with a has_one association and works great. Now I just need to make it into a many_to many association to be able to call @artist.groups. vs. @artist.group. Would a a join table now be necessary to get this to work?

class Artist < ActiveRecord::Base
  attr_accessor :group_id      

  has_many :members, class_name: 'Artist', foreign_key: 'group_id'
  belongs_to :group, class_name: 'Artist', foreign_key: 'group_id'
end
Community
  • 1
  • 1
Carl Edwards
  • 13,826
  • 11
  • 57
  • 119
  • do you have a class called `Group`? – GMA Nov 27 '14 at 04:33
  • No I'm using the `User` class to represent the group. – Carl Edwards Nov 27 '14 at 04:34
  • I don't know the specifics of your app, but that doesn't really make conceptual sense to me. How can one object represent both a User and a group of Users?. – GMA Nov 27 '14 at 04:35
  • Incidentally, I don't think you need to include `foreign_key: 'group_id'` for `belongs_to :group`. A `belongs_to` association will automatically look for the foreign key `name_of_association_id`, you only need to specify the foreign_key explicitly if you're using something different from the default. – GMA Nov 27 '14 at 04:36
  • Let me rephrase my code. I started out with an artist model (music) and group model. As I went further into development I found that both models had completely identical code. I decided that using a self join would make more sense. – Carl Edwards Nov 27 '14 at 04:39
  • Ah okay... so an artist could be "Eminem" or "D12", but Eminem is also part of D12. Makes sense now. – GMA Nov 27 '14 at 04:46

1 Answers1

1

You definitely need a join table for a many-to-many association.

Try something like this (this is from memory so may need some adjusting):

class Membership
  # attributes "artist_id" and "group_id"
  belongs_to :member, class_name: "Artist"
  belongs_to :group, class_name: "Artist"
end

class Artist
  # no foreign keys required
  has_many :group_memberships, class_name: "Membership", foreign_key: :group_id
  has_many :members, through: :group_memberships

  has_many :memberships, foreign_key: :member_id
  has_many :groups, through: :memberships
end

Then your view can stay the same as it is in the question.

You probably also want to add a unique index to your memberships table:

add_index :memberships, :member_id
add_index :memberships, :group_id
add_index :memberships, [:member_id, :group_id], unique: true

... with the appropriate validates_uniqueness_of validation in your Artist model.

GMA
  • 5,816
  • 6
  • 51
  • 80
  • I just edited this; My original answer didn't quite work ("groups" and "members" were named the wrong way round.) Try it again now. – GMA Nov 27 '14 at 05:06
  • Okay so managed to get the those attributes added to the join table. The only problem I'm running into is an error within the first conditional in the view: `SQLite3::SQLException: no such column: memberships.member: SELECT 1 AS one FROM "artists" INNER JOIN "memberships" ON "artists"."id" = "memberships"."group_id" WHERE "memberships"."member" = ? LIMIT 1` – Carl Edwards Nov 27 '14 at 05:19
  • Oops, yes, that was a typo. `foreign_key: :member` should be `foreign_key: :member_id`. I'll edit my answer. – GMA Nov 27 '14 at 06:35