habtm - stack too deep error

Hi all,

I am trying to create many-to-many relationship between models "Topic" and "Category"

I am trying following in rails console which gives me exception.

t1 = Topic.new(:title => "t1") c1 = Category.new(:name => "c1") t1.categories << c1 t1.save

Gives Exception........

Am I doing something really wrong?

Thanks, Siddharth

Here is my Topic model class Topic < ActiveRecord::Base   has_and_belongs_to_many :categories,     :class_name => "Category",     :join_table => "topics_categories",     :association_foreign_key => "category_id",     :foreign_key => "topic_id",     :after_add => :add_topic_to_category,     :after_remove => :remove_topic_from_category

    def add_topic_to_category(category)       category.topics << self unless category.topics.include?(self)     end     def remove_topic_from_category(category)       category.topics.delete(self) rescue nil     end end

and Category class Category < ActiveRecord::Base   has_and_belongs_to_many :topics,     :class_name => "Topic",     :join_table => "topics_categories",     :association_foreign_key => "topic_id",     :foreign_key => "category_id" end

and topics_categories table create table topics_categories (   topic_id bigint,   category_id bigint );

Full stack trace Loading development environment.

t1 = Topic.new(:title => "t1")

=> #<Topic:0xb766da88 @new_record=true, @attributes={"created_on"=>nil, "num_comments"=>nil, "title"=>"t1", "updated_on"=>nil, "url"=>"", "blocked"=>0, "description"=>"", "user_id"=>nil, "num_views"=>nil}>

c1 = Category.new(:name => "c1")

=> #<Category:0xb7614cd0 @new_record=true, @attributes={"name"=>"c1", "blocked"=>0, "description"=>"", "usage_count"=>nil}>

t1.categories << c1

=> [#<Category:0xb7614cd0 @topics=[#<Topic:0xb766da88 @categories=[...], @new_record=true, @attributes={"created_on"=>nil, "num_comments"=>nil, "title"=>"t1", "updated_on"=>nil, "url"=>"", "blocked"=>0, "description"=>"", "user_id"=>nil, "num_views"=>nil}>], @new_record=true, @attributes={"name"=>"c1", "blocked"=>0, "description"=>"", "usage_count"=>nil}>]

t1.save

SystemStackError: stack level too deep         from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.4/lib/active_record/associations.rb:912:in `validate_associated_records_for_categories'         from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.4/lib/active_record/validations.rb:802:in `run_validations'         from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.4/lib/active_record/validations.rb:800:in `run_validations'         from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.4/lib/active_record/validations.rb:764:in `valid_without_callbacks'         from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.4/lib/active_record/callbacks.rb:310:in `valid?'         from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.4/lib/active_record/associations.rb:918:in `validate_associated_records_for_topics'         from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.4/lib/active_record/associations/association_proxy.rb:110:in `method_missing'         from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.4/lib/active_record/associations/has_and_belongs_to_many_association.rb:81:in `method_missing'         from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.4/lib/active_record/associations.rb:913:in `validate_associated_records_for_topics'         from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.4/lib/active_record/validations.rb:802:in `run_validations'         from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.4/lib/active_record/validations.rb:800:in `run_validations'         from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.4/lib/active_record/validations.rb:764:in `valid_without_callbacks'         from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.4/lib/active_record/callbacks.rb:310:in `valid?'         from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.4/lib/active_record/associations.rb:918:in `validate_associated_records_for_categories'         from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.4/lib/active_record/associations/association_proxy.rb:110:in `method_missing'         from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.4/lib/active_record/associations/has_and_belongs_to_many_association.rb:81:in `method_missing' ... 2796 levels...         from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.4/lib/active_record/callbacks.rb:310:in `valid?'         from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.4/lib/active_record/associations.rb:918:in `validate_associated_records_for_categories'         from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.4/lib/active_record/associations/association_proxy.rb:110:in `method_missing'         from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.4/lib/active_record/associations/has_and_belongs_to_many_association.rb:81:in `method_missing'         from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.4/lib/active_record/associations.rb:913:in `validate_associated_records_for_categories'         from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.4/lib/active_record/validations.rb:802:in `run_validations'         from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.4/lib/active_record/validations.rb:800:in `run_validations'         from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.4/lib/active_record/validations.rb:764:in `valid_without_callbacks'         from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.4/lib/active_record/callbacks.rb:310:in `valid?'         from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.4/lib/active_record/validations.rb:735:in `save_without_transactions'         from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.4/lib/active_record/transactions.rb:126:in `save'         from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.4/lib/active_record/connection_adapters/abstract/database_statements.rb:51:in `transaction'         from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.4/lib/active_record/transactions.rb:91:in `transaction'         from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.4/lib/active_record/transactions.rb:118:in `transaction'         from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.4/lib/active_record/transactions.rb:126:in `save'         from (irb):4>>

Siddharth Karandikar wrote:

I am trying to create many-to-many relationship between models "Topic" and "Category"

Check to ensure that your model file is not being required twice. I have had this before when the habtm macro is defined twice by accident because the model was being required twice.

Eric

Siddharth Karandikar wrote: > I am trying to create many-to-many relationship between models "Topic" > and "Category"

Check to ensure that your model file is not being required twice. I have had this before when the habtm macro is defined twice by accident because the model was being required twice.

Thanks Eric for reply.

I far as I know, Rails handles all the "requires" part, so how should we add a check there? Is this rails console specific issue?

- Siddharth

here is your problem:

from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.4/lib/active_record/associations.rb:913:in `validate_associated_records_for_topics'

from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.4/lib/active_record/associations.rb:913:in `validate_associated_records_for_categories'

from this i assume you left out the validations from your models in your post as this suggests you have validates_associated :topics in your Category model and validates_associated :categories in your Topic model.

this is a big no-no as is causes exactly what you are experiencing, an infinite validation loop. when you save your topic it tries to validate the associated category which then tries to validate the associated topic which then tries to validate the associated category and so on.

here is your problem:

from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.4/lib/active_record/associations.rb:913:in `validate_associated_records_for_topics'

from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.4/lib/active_record/associations.rb:913:in `validate_associated_records_for_categories'

from this i assume you left out the validations from your models in your post as this suggests you have validates_associated :topics in your Category model and validates_associated :categories in your Topic model.

this is a big no-no as is causes exactly what you are experiencing, an infinite validation loop. when you save your topic it tries to validate the associated category which then tries to validate the associated topic which then tries to validate the associated category and so on.

Thanks Chris for pointing out the real cause. It has to be because of infinite loop. But if you see the model classes that I have posted in my first mail, those doesn't contain any of these "validates_associated" methods. So rails is trying to do this validations on its own.

Now the question is, how to tell rails not to validate for these things?

- सिद्धार्थ [Siddharth] [written using www.paahijen.com]

Shit,

You really over complicated this.

rename your table to categories_topics and

> class Topic < ActiveRecord::Base > has_and_belongs_to_many :categories, > :class_name => "Category", > :join_table => "topics_categories", > :association_foreign_key => "category_id", > :foreign_key => "topic_id", > :after_add => :add_topic_to_category, > :after_remove => :remove_topic_from_category

becomes

class Topic < ActiveRecord::Base    has_and_belongs_to_many :categories end

Thanks for showing me the simpler version. But I doubt this will solve the "stack too deep" issue.

- Siddharth

I agree with Keynan on this, although I probably would have said it differently. :slight_smile:

might I suggest that you remove the :after_add callback from the habtm and see what happens? I don't see a need to have that callback because the relationship will exist because you just added it. doesn't matter if you create the relationship via Topic or Category, it's still the same relationship. same goes for your after_remove callback. you'd just be trying to remove the relationship you just removed.

So my suggestion is this

Category < ActiveRecord::Base   has_and_belongs_to_many :topics,       # this line not necessary if you name join table "categories_topics"       :join_table => "topics_categories",       :uniq => true end

Topic < ActiveRecord::Base   has_and_belongs_to_many :categories,       # this line not necessary if you name join table "categories_topics"       :join_table => "topics_categories",       :uniq => true end

try this out and see what happens

Cool. That works!

Hmm .. then I need to see why that's been done in one of the rails recipes chapter. :slight_smile: Anyways, thanks guys.

- Siddharth