HABTM & Many through association.

Hi there,

I am trying to create some sort of a blog and i'm trying to create is a method that takes care of the creation/removal of the records in the join table.

So i have a blog that has many "tags" (join table)

I will let the code speak for itself.

#technology model has_many :tags, :dependent => :destroy has_many :blogs, :through => :tags

#Tag model belongs_to :technology belongs_to :blog

#blog model has_many :tags, :dependent => :destroy has_many :technologies, :through => :tags

def tagging=(tagging)   tags.each do |tag|     tag.destroy unless tagging.include? (tag.blog_id.to_i)   end   tagging.each do |tag|     self.tags.create(:technology_id => tag) unless tags.any?{|t| t.technology_id.to_i == tag.to_i}   end end

#blog controller def create   params[:blog][:tagging] ||=   ... end

#blog form <div id="technology_form">   <font class=blog_technologies>Blog technologies</font>   <p>   <% for technology in @technologies %>       <%= check_box_tag "blog[tagging]", technology.id,               @blog.technologies.include?(technology) %>       <%= technology.name %>       <br />   <% end %>   </p> </div> ....

I know i have to write the tagging method that will create or delete the "tags", and it's working only when im creating a tag, but when i delete it shows an error:

Mysql::Error: #42S22Unknown column 'id' in 'where clause': DELETE FROM `tags`             WHERE `id` = NULL

I know i don't have an id for the join table

class CreateTags < ActiveRecord::Migration   def self.up     create_table :tags, :id => false do |t|       t.integer :blog_id       t.integer :technology_id ......   end end

How can i solve this problem? thanks in advanced

RoR_Nb wrote:

def tagging=(tagging)   tags.each do |tag|     tag.destroy unless tagging.include? (tag.blog_id.to_i)   end   tagging.each do |tag|     self.tags.create(:technology_id => tag) unless tags.any?{|t| t.technology_id.to_i == tag.to_i}   end end

....

try to use has_and_belongs_to_many association if your intermediate table has only two columns (foreign_keys)

if you want to delete associated record directly then assign @blog.tags= it will aiotumatically delete those records don't use tag.destroy

otherwise use has_many :through association with a primary key (remove :id=>false)

Thank you Pokkai Dokkai for your reply, i have 3 questions:

1) I am using has_many :through association because i want the association to be open for more fields in case i need it to.

2) I am already setting up the an empty array in the blogs controller (in the update and the new action) before it runs the tagging method, isn't that supposed to work or is it that rails will automatically take care of it?

params[:blog][:tagging] ||= #This is how i'm setting the empty array in case that none of the checkboxes was selected.

3) What is the difference between the has_many :through association with the primary key and without it?

I know i don't have an id for the join table

With has_many :through, the join table is a model in its own right and
needs a promary key

Fred

It did remove the mysql error thanks a lot guys. but there's still a problem, and what happens is that if i have 2 tags selected and then i unchecked 1 then it deletes both, maybe it has to do with the code. but i guess it's my login what's not working, is that correct?.

I'll work on that and if i find a solution i'll post it here for future other people to see it.

Lovely

This might be a php solution but it works:

def tagging=(tagging)     Tag.delete_all "technology_id not in (" + tagging.to_s + ") and blog_id = " +self.id.to_s     tagging.each do |tag|       self.tags.create(:technology_id => tag) unless tags.any?{|t| t.technology_id.to_i == tag.to_i}     end   end

The previous code fails when creating new blog this works

def tagging=(tagging)     Tag.delete_all "technology_id not in (" + tagging.to_s + ") and blog_id = " +self.id.to_s unless self.new_record?     tagging.each do |tag|       tags.build(:technology_id => tag) unless tags.any?{|t| t.technology_id.to_i == tag.to_i}     end   # Tag.delete_all "technology_id not in (" + tagging.to_s + ") and blog_id = " +self.id.to_s unless self.new_record?   # tagging.each do |tag|   # self.tags.create(:technology_id => tag) unless tags.any?{|t| t.technology_id.to_i == tag.to_i}   # end end

It seems php but it works

I think the reason is because you need to build the data in memory because in the moment "def tagging=(tagging)" method setter is run the blog has not been created and that's why it should go in memory.

that's basically going above rails, using mysql where clause but with rails:

  def tagging=(tagging)     self.tags.each do |tag|       tag.destroy unless tagging.include? (tag[:technology_id].to_s)     end     tagging.each do |tag|       tags.build(:technology_id => tag) unless tags.any?{|t| t.technology_id.to_i == tag.to_i}     end   end

def tagging=(tagging)

tags.each do |tag|    tag.destroy unless tagging.include? (tag.blog_id.to_i) end

tagging is an array of strings, so this will always destroy all tags.

Fred