Reputation: 334
I currently have these models:
class Base < ActiveRecord::Base
has_many :base_tags
has_many :tags, through: :base_tags
has_many :primary_tags, through: :base_tags, class_name: 'Tag', source: :tag, conditions: ['base_tags.primary = ?', true]
has_and_belongs_to_many :channels
end
class Tag < ActiveRecord::Base
has_many :base_tags
has_many :bases, through: :base_tags
end
class BaseTag < ActiveRecord::Base
belongs_to :base
belongs_to :tag
end
I'm struggling to implement the primary tags on the base model. Calling #primary_tags on a base instance retuns the correct records but whilst trying to create/update the record:
Base.create({tag_ids: [1,2], primary_tag_ids: [1]})
I'm running into the following error:
ActiveRecord::RecordNotUnique
Mysql2::Error: Duplicate entry '1-2' for key 'index_bases_tags_on_base_id_and_tag_id': INSERT INTO `base_tags` (`base_id`, `primary`, `tag_id`) VALUES (1, 0, 2)
ActiveRecord is trying to create the same association for the primary_tag_ids as the tag_ids when it should really be updating the relation and the primary attribute should be 1.
Is there any way of getting ActiveRecord to play nice? I imagine my has_many :primary_tags relation is incorrect.
Upvotes: 0
Views: 226
Reputation: 84182
I think there are two problems.
Firstly, if you use the string form of conditions, then activerecord isn't able to parse that sql fragment and understand what attributes it should set.
Secondly, because this is a has many through, I think those conditions need to be on the join model association
With the model like so
class Base < ActiveRecord::Base
has_many :base_tags
has_many :primary_base_tags, conditions: {primary: true}, class_name: 'BaseTag'
has_many :tags, through: :base_tags
has_many :primary_tags, through: :primary_base_tags, class_name: 'Tag', source: :tag
end
Then activerecord sets the primary flag properly for me.
Upvotes: 2
Reputation: 9443
This solution might work for you. I also added an alternative version of accessing primary_tags
, but since your version works, you probably don't need it.
class Base < ActiveRecord::Base
has_many :base_tags
has_many :tags, through: :base_tags
has_and_belongs_to_many :channels
def primary_tags
self.tags.includes(:base_tags).where(base_tags: { primary: true } )
end
def primary_tags_ids=(ids)
current_ids = self.base_tags.map(&:tag_id)
ids.each do |id|
if current_ids.include?(id)
self.base_tags.select { |bt| bt.tag_id == id }.first.primary = true
else
self.base_tags.build( { tag_id: id, primary: true } )
end
end
end
end
Upvotes: 0