Help with Self-Referential HABTM


#1

With the help of the Rails Recipes have got a self-referential HABTM
relationship working, but I’m really struggling with getting a new entry
from a form accepted. i should say here that the form is also submitting
info for the join table too.

So in my model I have:

class Type < ActiveRecord::Base
has_and_belongs_to_many :subtypes,
:class_name => ‘Type’,
:join_table => ‘subtypes_types’,
:association_foreign_key => ‘subtype_id’,
:foreign_key => ‘type_id’

Then in the form I have this (@subtype carries the detail of the a
parent – so this form is called by a link “create a new subtype from
this type”. NB each type can have many subtypes and belong to many types
to – hence needs to be many-many rather than a tree structure):

<%= start_form_tag :action => ‘createsubtype’, :id => @type %>
<%= render_partial ‘form’ %> # Gets all the info for the Type fields
<%#= hidden_field(‘type’, ‘subtype_id’, “value” => @subtype.id) %>
<%= hidden_field(‘subtypes’, ‘id’, “value” => @subtype.id) %>
<%= submit_tag ‘Create’ %>
<%= end_form_tag %>

In the controller I’ve got:
def createsubtype
@type = Type.new(params[:type])
@type.subtypes = Subtype.find(params[:subtype_id]) #THIS LINE IS THE
PROB
if @type.save
flash[:notice] = ‘Type was successfully created.’
redirect_to :action => ‘list’
else
render :action => ‘new’
end
end

But the problem line causes an error:

uninitialized constant Subtype

…presumably because there’s no model for Subtype.
Have tried referring to it via Type [still VERY much a noob, so prob
making basic error here]:

@type.subtypes = Type.subtypes.find(params[:subtype_id])

But then I get:
undefined method `subtypes’ for Type:Class

Can someone please show me the error of my ways…
Thanks
Chris T


#2

OK. Got this working now. May help some other clueless newbie banging
his head against a brick wall like I did. [For the record, solved it
through a combination 2 parts Agile book, 1/2 part Rails Recipes, 1 part
Rails Weenie – specifically

http://rails.techno-weenie.net/question/2006/2/22/simple_controller_for_saving_habtm_data_in_join_table
– and 1/2 part figuring it out. VERY frustrating, but I have to say
figuring it out yourslef definitely means you understand things a lot
better at the end].

Basically I replaced:

@type = Type.new(params[:type])
@type.subtypes = Subtype.find(params[:subtype_id])  

with:

@parenttype = Type.find(params[:parenttype][:id])
@parenttype.subtypes << Type.create(params[:type])

and

<%= start_form_tag :action => ‘createsubtype’, :id => @type %>
<%= render_partial ‘form’ %> # Gets all the info for the Type fields
<%#= hidden_field(‘type’, ‘subtype_id’, “value” => @subtype.id) %>
<%= hidden_field(‘subtypes’, ‘id’, “value” => @subtype.id) %>
<%= submit_tag ‘Create’ %>
<%= end_form_tag %>

with:

<%= start_form_tag :action => ‘createsubtype’, :id => @type %>
<%= render_partial ‘form’ %> # Gets all the info for the Type fields
<%= hidden_field(‘parenttype’, ‘id’, “value” => @parenttype.id) %>
<%= submit_tag ‘Create’ %>
<%= end_form_tag %>

As you can see, I changed the name of the parameter being passed by the
form to parenttype – not relevant in getting it working, just a better
description of what it is – and then with

@parenttype = Type.find(params[:parenttype][:id])

got the Type object with the id of the parent. Then with

@parenttype.subtypes << Type.create(params[:type])

pushed the new Type (the one where all the data was filled in with the
partial form, and which is the subtype (child) of the parent) onto the
parenttype as its subtype, and thus saving both it and the relationship
at the same time.

[I also separately added a parenttype to the class to make it easier (well, clearer) navigating in the opposite direction in normal usage].

Hope this is clear to (and hels) any newbs struggling with HABTM,
particularly self-referential ones.

Chris T wrote:

With the help of the Rails Recipes have got a self-referential HABTM
relationship working, but I’m really struggling with getting a new entry
from a form accepted. i should say here that the form is also submitting
info for the join table too.

So in my model I have:

class Type < ActiveRecord::Base
has_and_belongs_to_many :subtypes,
:class_name => ‘Type’,
:join_table => ‘subtypes_types’,
:association_foreign_key => ‘subtype_id’,
:foreign_key => ‘type_id’

Then in the form I have this (@subtype carries the detail of the a
parent – so this form is called by a link “create a new subtype from
this type”. NB each type can have many subtypes and belong to many types
to – hence needs to be many-many rather than a tree structure):

<%= start_form_tag :action => ‘createsubtype’, :id => @type %>
<%= render_partial ‘form’ %> # Gets all the info for the Type fields
<%#= hidden_field(‘type’, ‘subtype_id’, “value” => @subtype.id) %>
<%= hidden_field(‘subtypes’, ‘id’, “value” => @subtype.id) %>
<%= submit_tag ‘Create’ %>
<%= end_form_tag %>

In the controller I’ve got:
def createsubtype
@type = Type.new(params[:type])
@type.subtypes = Subtype.find(params[:subtype_id]) #THIS LINE IS THE
PROB
if @type.save
flash[:notice] = ‘Type was successfully created.’
redirect_to :action => ‘list’
else
render :action => ‘new’
end
end

But the problem line causes an error:

uninitialized constant Subtype

…presumably because there’s no model for Subtype.
Have tried referring to it via Type [still VERY much a noob, so prob
making basic error here]:

@type.subtypes = Type.subtypes.find(params[:subtype_id])

But then I get:
undefined method `subtypes’ for Type:Class

Can someone please show me the error of my ways…
Thanks
Chris T