aoichan
aoichan

Reputation: 13

has_many :through create duplicates in join table

I created 3 models, User, City, UserCity.

User class:

class User < ActiveRecord::Base
  has_many :user_cities, :dependent => :destroy, :uniq => true
  has_many :cities, :through => :user_cities
end

City class:

class City < ActiveRecord::Base
  has_many :user_cities, :dependent => :destroy, :uniq => true
  has_many :users, :through => :user_cities
end

UserCity class:

class UserCity < ActiveRecord::Base
  belongs_to :user
  belongs_to :city
end

And then I tried

u = User.new()
c = City.new()
u.cities << c
c.users << u
p UserCity.all.size # => 2

user_cities table had duplicates. So then, I coded

UserCity class:

class UserCity < ActiveRecord::Base
  validates :user_id, :uniqueness => {:scope => :city_id}
  belongs_to :user
  belongs_to :city
end

and run the same ruby code above. But it failed after c.users << u because I prohibited duplicate.

How can I make u.cities have c and c.users have u without duplicating data in join table?


Added:

So, if I choose only c.users << u, can I only do this for cities?

cities = Array.new()
UserCity.where(:user_id => u.id).each do |uc|
  cities << City.find(uc.city_id)
end

Upvotes: 1

Views: 559

Answers (2)

lacostenycoder
lacostenycoder

Reputation: 11196

You might also want to prevent duplicates in the db by adding an index in the db which would raise an error if you accidentally try to add a duplicate join entry. Do something like this in a migration:

add_index :cities_users, [:city_id, :user_id], unique: true

Upvotes: 0

datalost
datalost

Reputation: 3773

Choose either u.cities << c or c.users << u. Each of them cause a row inserted in to the join table.

Upvotes: 2

Related Questions