how to avoid duplicates in a has_many :through relationship?
Simpler solution that's built into Rails:
class Blog < ActiveRecord::Base has_many :blogs_readers, :dependent => :destroy has_many :readers, :through => :blogs_readers, :uniq => true end class Reader < ActiveRecord::Base has_many :blogs_readers, :dependent => :destroy has_many :blogs, :through => :blogs_readers, :uniq => true end class BlogsReaders < ActiveRecord::Base belongs_to :blog belongs_to :reader end
Note adding the :uniq => true
option to the has_many
call.
Also you might want to consider has_and_belongs_to_many
between Blog and Reader, unless you have some other attributes you'd like to have on the join model (which you don't, currently). That method also has a :uniq
opiton.
Note that this doesn't prevent you from creating the entries in the table, but it does ensure that when you query the collection you get only one of each object.
Update
In Rails 4 the way to do it is via a scope block. The Above changes to.
class Blog < ActiveRecord::Base has_many :blogs_readers, dependent: :destroy has_many :readers, -> { uniq }, through: :blogs_readersendclass Reader < ActiveRecord::Base has_many :blogs_readers, dependent: :destroy has_many :blogs, -> { uniq }, through: :blogs_readersendclass BlogsReaders < ActiveRecord::Base belongs_to :blog belongs_to :readerend
Update for Rails 5
The use of uniq
in the scope block will cause an error NoMethodError: undefined method 'extensions' for []:Array
. Use distinct
instead :
class Blog < ActiveRecord::Base has_many :blogs_readers, dependent: :destroy has_many :readers, -> { distinct }, through: :blogs_readersendclass Reader < ActiveRecord::Base has_many :blogs_readers, dependent: :destroy has_many :blogs, -> { distinct }, through: :blogs_readersendclass BlogsReaders < ActiveRecord::Base belongs_to :blog belongs_to :readerend
This should take care of your first question:
class BlogsReaders < ActiveRecord::Base belongs_to :blog belongs_to :reader validates_uniqueness_of :reader_id, :scope => :blog_idend
The Rails 5.1 way
class Blog < ActiveRecord::Base has_many :blogs_readers, dependent: :destroy has_many :readers, -> { distinct }, through: :blogs_readersendclass Reader < ActiveRecord::Base has_many :blogs_readers, dependent: :destroy has_many :blogs, -> { distinct }, through: :blogs_readersendclass BlogsReaders < ActiveRecord::Base belongs_to :blog belongs_to :readerend