Some strange behavior for has_many with STI


#1

Hi all,

I’m having some trouble with a has_many association on a table using
the single table inheritance model that I’m hoping someone can help me
with. The schema for the table is:

CREATE TABLE comments (
id int(11) NOT NULL auto_increment,
user_id int(11) default NULL,
type varchar(20) NOT NULL default ‘’,
type_id int(11) NOT NULL default ‘0’,
name varchar(255) default NULL,
comment text NOT NULL,
created_at datetime default NULL,
updated_at datetime default NULL,
PRIMARY KEY (id),
KEY FK_users_comments (user_id)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

ALTER TABLE comments
ADD FOREIGN KEY (user_id) REFERENCES users (id);

on top of which, I’ve got two tables: events and activities, both of
which can have comments. Here are the model associations for Events,
Activities and Comments:

class Activity < ActiveRecord::Base
belongs_to :user
has_many :comments, :conditions => “type = ‘Activity’”, :foreign_key
=> “type_id”
acts_as_taggable :join_class_name => “TagActivity”

class Event < ActiveRecord::Base
include EventMatching
belongs_to :activity_type
has_and_belongs_to_many :users
has_many :comments, :conditions => “type = ‘Event’”, :foreign_key =>
“type_id”

class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :activity, :conditions => “type = ‘Activity’”,
:foreign_key => “type_id”
belongs_to :event, :conditions => “type = ‘Event’”, :foreign_key =>
“type_id”

The strange behaviour is this: for activities, everything works
perfectly. I can do:

render(:partial => “shared/comment”, :collection => @activity.comments)

And it works like a charm. When I do the same for events:

render(:partial => “shared/comment”, :collection => @event.comments)

I get an error: undefined method `user’ for #Event:0x382e3a8. This
error is on the first line that accesses comment.user in the partial.

Here is the SQL I’m seeing in the development.log for fetching
comments for Activities and Events:

SELECT * FROM comments WHERE (comments.type_id = 59 AND (type =
‘Activity’))
SELECT * FROM comments WHERE (comments.type_id = 8 AND (type = ‘Event’))

Both queries return the correct rows when executed directly against
the DB (MySQL 5 btw). The really odd thing though, is if I comment
out the belongs_to :user from the Activity model, then Activity starts
throwing the same error as Event. Somehow it looks like the
association from Comment to User is relying upon the parent object
also having an association to user (even though Event does have a
h_a_b_t_m :users, it’s still breaking), and even though the Comment is
relying on the parent’s relationship, it’s still pulling in the proper
user for each individual comment.

This has had me stumped for the better part of the day, so hopefully
someone might have some insight. If there’s any relevant info or code
I’ve left out let me know and I’ll be glad to share it.

Thanks in advance,

Joshua


#2

That’s not quite how STI is supposed to be used. What you should do is
something like:

class Comment < ActiveRecord::Base
end

class EventComment < Comment
end

class ActivityComment < Comment
end

class Event < ActiveRecord::Base
has_many :comments, :class_name => ‘EventComment’
end

class Activity < ActiveRecord::Base
has_many :comments, :class_name => ‘ActivityComment’
end

Throw an event_comment_id and activity_comment_id on the comments table,
and
remove type_id.

Haven’t tried it but it should do what you’re after.

The user error you are getting is because there is no ‘user’ association
on
Event. You have used a belongs_to on Activity, and
has_and_belongs_to_many
on Event. You need to access Event.users instead.

-Jonathan.