HABTM & Many through association


#1

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

Blog technologies

<% for technology in @technologies %> <%= check_box_tag "blog[tagging][]", technology.id, @blog.technologies.include?(technology) %> <%= technology.name %>
<% end %>

....

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


#2

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)


#3

Thank you Pokkai D. 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.

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

On Oct 5, 11:57 pm, Pokkai D. removed_email_address@domain.invalid


#4

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.

On Oct 7, 3:33 am, Frederick C. removed_email_address@domain.invalid


#5

On 6 Oct 2008, at 07:43, RoR_Nb wrote:

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


#6

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


#7

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


#8

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


#9

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.


#10

On 6 Oct 2008, at 07:43, RoR_Nb wrote:

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