JZ.
JZ.

Reputation: 21877

Two or more Many to Many relationships between two tables in rails

I have two tables:

Users and Groups

a User has_many groups and a group, has_many a users:

u = User.last
u.groups
g = Group.last
g.users

Supposed I wanted a second list of different groups, for some strange reason. Where once again a User has may groups (called other_group in this example) and a group has many users.

u = User.last
u.other_groups
g = Group.last
g.other_users

How do I associate two models in this relationship, twice using Active Record? Do I need multiple has and belongs to many tables? perhaps a has and belongs to many "through". What does this look like?


Answer:

class Matter < ActiveRecord::Base
  has_many :matters_lawfirms
  has_many :matters_other_lawfirms
  has_many :lawfirms, class_name: 'Lawfirm', through: :matters_lawfirms,  :source => :lawfirm
  has_many :other_lawfirms, class_name: 'Lawfirm', through: :matters_other_lawfirms,  :source => :lawfirm
end

class Lawfirm < ActiveRecord::Base
  has_many :matters_lawfirms
  has_many :matters_other_lawfirms
  has_many :matters, class_name: 'Matter', through: :matters_lawfirms,  :source => :matter
  has_many :other_matters, class_name: 'Matter', through: :matters_other_lawfirms,  :source => :matter
end

class MattersLawfirm < ActiveRecord::Base
  belongs_to :matter
  belongs_to :lawfirm
end

class MattersOtherLawfirm < ActiveRecord::Base
  belongs_to :matter
  belongs_to :lawfirm
end

migrations:

class AddMatterOtherLawfirms < ActiveRecord::Migration
  def change
    create_table :matters_other_lawfirms, :id => false do |t|
      t.references :matter, :lawfirm
    end

    add_index :matters_other_lawfirms, [:matter_id, :lawfirm_id],
      name: "matters_other_lawfirms_index",
      unique: true
  end
end



class AddMatterLawfirmsHabtmt < ActiveRecord::Migration
  def change
    create_table :matters_lawfirms, :id => false do |t|
      t.references :matter, :lawfirm
    end

    add_index :matters_lawfirms, [:matter_id, :lawfirm_id],
      name: "matters_lawfirms_index",
      unique: true
  end
end

Upvotes: 0

Views: 2036

Answers (2)

ConnorCMcKee
ConnorCMcKee

Reputation: 1645

Assuming you already know how to handle has_many relationships in your data model (the point to which Alper appears to be writing), it's quite easy to accomodate multiple relationships between two tables (I do something right now which involves linking users distinctly to a projects on which they are working, and to projects which they own. I believe this is very similar to what you're seeking to accomplish). The code would look something like this:

User Model

has_many :regular_groups, class_name: 'Group', through: :user_regular_groups
has_many :other_groups, class_name: 'Group', through: :user_other_groups

Group Model

has_many :regular_users, class_name: 'User', through: :user_regular_groups
has_many :other_users, class_name: 'User', through: :user_other_groups

Obviously in this case we're using two distinct association tables (user_regular_groups and user_other_groups), but you could accomplish something similar with one using a scope (along the lines of what Alper is recommending).

I hope that helps!

Upvotes: 0

Alper Karapınar
Alper Karapınar

Reputation: 2694

You simply can't define many to many relationship using belong_to

You should implement has_and_belongs_to_many or has_many :through relationship instead of has_many - belongs_to relationship.

EDIT

Ok i think i get it now,

You can't achive this using a single has_and_belongs_to_many table, i'd go for a has_many :through relation. If you have only two group category, set a flag for that category in your join table.

Not tested yet, but something similar should work

class GroupMembership #.. with category field or something
  belongs_to :group
  belongs_to :user

class User
  has_many :group_memberships
  has_many :local_groups, -> { where group_memberships: { category: 'local' } }, :through => :group_memberships, :source => :group
  has_many :outside_groups, -> { where group_memberships: { category: 'outside' } }, :through => :group_memberships, :source => :group

class Group
  has_many :group_memberships
  has_many :local_users, -> { where group_memberships: { category: 'local' } }, :through => :group_memberships, :source => :user
  has_many :outside_users, -> { where group_memberships: { category: 'outside' } }, :through => :group_memberships, :source => :user

For the HABTM relationship, you need to define more then one join table.

Upvotes: 0

Related Questions