# Has\_many :through polymorphic with STI in the polymorphic target tables

**URL:** <https://www.ruby-forum.com/t/has-many-through-polymorphic-with-sti-in-the-polymorphic-target-tables/137002>\
**Category:** Rails\
**Created:** [May 8, 2008, 4:22am UTC](https://www.ruby-forum.com/t/has-many-through-polymorphic-with-sti-in-the-polymorphic-target-tables/137002 "2008-05-08T04:22:24Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![makkura](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/m/76d3ee/32.png) [@makkura](https://www.ruby-forum.com/u/makkura)\
**Post date:** [May 8, 2008, 4:22am UTC](https://www.ruby-forum.com/t/has-many-through-polymorphic-with-sti-in-the-polymorphic-target-tables/137002/1 "2008-05-08T04:22:24Z")

</div>

has\_many :through polymorphic with STI in the polymorphic target  
tables

Hello,  
In our data model we have Users that can be members of Schools,  
Organizations, etc.  
The Schools table uses STI to support Colleges and High Schools,

The has\_many code is pretty straightforward, here is the school side  
(school\_memberships inherits from memberships):  
(all the models and schema at the bottom or  
[Parked at Loopia](http://pastie.caboo.se/193409))

class School \< ActiveRecord::Base  
has\_many :school\_memberships, :as =\> :member  
has\_many :users, :through =\> :school\_memberships  
end

Now if I do:

some\_school.users it runs the following query:  
SELECT users.\* FROM users  
INNER JOIN memberships ON users.id = memberships.user\_id  
WHERE ((memberships.member\_type = ‘School’) AND (memberships.member\_id  
= 3) AND ((memberships.type = ‘SchoolMembership’)))

The problem here is that there is no need for a  
memberships.member\_type column, the memberships.type column is  
handling the polymorphism of the table. If I add this column in the  
migration anyway, it causes a problem because Rails doesn’t populate  
it reliably (it populates it if you add a user to a school, but not if  
you add a school to a user – some\_user.schools \<\< some\_school).

My question, then, is how can I get Rails to not use the  
memberships.member\_type column without overriding the built in  
methods?

I looked for people doing this and found some similar things:  
Josh S. -  
[has\_many :through - The other side of polymorphic :through associations](http://blog.hasmanythrough.com/2006/4/3/polymorphic-through),  
and  
Pratik Naik - [http://m.onkey.org/2007/8/14/excuse-me-wtf-is-polymorphs](http://m.onkey.org/2007/8/14/excuse-me-wtf-is-polymorphs)  
but they are focused on making polymorphism work at all (i.e. joining  
to multiple tables) and my problem seems to stem from having one of  
the target tables have STI on it.

Can anyone point me to where to look for anything on this, or to  
someone who has done it?  
Here’s what works and does not work when I keep the membership\_type  
column in more detail:

a\_school.users \<\< a\_user  
it works perfectly and inserts a SchoolMembership into the memberships  
table  
#\<SchoolMembership id: 1, user\_id: 1, type: “SchoolMembership”,  
member\_id: 2, member\_type: “School”\>  
(in this case: #\<User id: 1, name: “Joseph”\> and #\<College id: 2,  
name: “NYU”, type: “College”\> )

- here School puts “School” in member\_type and “SchoolMembership” in  
the type

But, if I do:  
a\_user.schools \<\< a\_school  
the SchoolMembership that gets created does not have a member\_type:  
#\<SchoolMembership id: 1, user\_id: 1, type: “SchoolMembership”,  
member\_id: 2, member\_type: nil\>

- User only puts “SchoolMembership” in the type

The Code:

class User \< ActiveRecord::Base  
has\_many :school\_memberships  
has\_many :schools, :through =\> :school\_memberships  
end

class School \< ActiveRecord::Base  
has\_many :school\_memberships, :as =\> :member  
has\_many :users, :through =\> :school\_memberships  
end

# Table name: schools

# 

# id :integer

# name :string

# type :string

class College \< School  
end

class Membership \< ActiveRecord::Base  
belongs\_to :user  
end

# Table name: memberships

# 

# id :integer

# member\_id :integer

# member\_type :string

# type :string

class SchoolMembership \< Membership  
belongs\_to :school, :foreign\_key =\> :member\_id  
end

---

<div class="post-metadata">

**Author:** ![makkura](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/m/76d3ee/32.png) [@makkura](https://www.ruby-forum.com/u/makkura)\
**Post date:** [May 8, 2008, 5:05am UTC](https://www.ruby-forum.com/t/has-many-through-polymorphic-with-sti-in-the-polymorphic-target-tables/137002/2 "2008-05-08T05:05:09Z")

</div>

Hi,

Okay… first of all, if you don’t have a _darn_ good reason for using  
STI on your memberships table then get rid of it.

Second of all, you’re missing some things to tell rails about your  
polymorphism. Take a look at the following code (untested, off the  
top of my head):

class Membership  
belongs\_to :user  
belongs\_to :member, :polymorphic =\> true  
end

Note - that means that membership belongs to a user (duh!) and to a  
‘member’ which can be _anything_ - School, Business, Club,  
FluffyKitten - _any_ active record object.

class User  
has\_many :memberships  
has\_many :schools, :through =\> :memberships, :source =\> :member,  
:source\_type =\> ‘School’  
has\_many :clubs, :through =\> :memberships, :source =\> :member,  
:source\_type =\> ‘Club’  
has\_many :fluffy\_kittens, :through =\> :memberships, :source =\>  
:member, :source\_type =\> ‘FluffyKitten’  
end

class School  
has\_many :memberships, :as =\> :member  
has\_many :users, :through =\> :memberships  
end

class Club  
has\_many :memberships, :as =\> :member  
has\_many :users, :through =\> :memberships  
end

class FluffyKitten … okay, you get the idea.

Basically, if you want to use a polymorphic belongs\_to - you have to  
actually _declare_ it as such if you want the free setup of the  
polymorph\_type field.

I can understand why you went down your original route - and if you  
_really_ know what you’re doing you can torture ActiveRecord enough to  
do your bidding. But you’d be fighting the framework and it would be  
a source of confusion and pain.

IMHO, of course.

Hope that helps,  
Trevor

On 5/7/08, Joseph S. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> (school\_memberships inherits from memberships):  
> SELECT users.\* FROM users
> 
> the target tables have STI on it.  
> #\<SchoolMembership id: 1, user\_id: 1, type: “SchoolMembership”,  
> member\_id: 2, member\_type: nil\>  
> end
> 
> # name :string
> 
> end
> 
> >

## –

Trevor S.  
[http://somethinglearned.com](http://somethinglearned.com)

---

<div class="post-metadata">

**Author:** ![makkura](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/m/76d3ee/32.png) [@makkura](https://www.ruby-forum.com/u/makkura)\
**Post date:** [May 8, 2008, 10:09pm UTC](https://www.ruby-forum.com/t/has-many-through-polymorphic-with-sti-in-the-polymorphic-target-tables/137002/3 "2008-05-08T22:09:35Z")

</div>

Hi Trevor,  
Thanks a lot for your reply,  
I think I’ve tried it that way and still had troubles.  
It seems like the STI in the memberships table is fine and works  
smoothly - the STI in the target table is the headache.  
The type column in memberships is not the issue,  
it’s the member\_type column that doesn’t seem to work correctly.

It’s really interesting that actually I don’t think you need the  
:polymorphic =\> true part.  
I’ve put that in my membership model before and it doesn’t seem to  
change anything (if I hit ‘undo’ once it pops back in so I’ve been  
playing with it a lot).  
In fact if you read through the comments on Josh S.'s has\_many  
through blog post about it someone mentions that (it is however 2  
years  
old):

FranÃƒÂ§ois Simond – 2006-04-22 15:25:09 :  
“While experimenting i just found that belongs\_to :publication,  
:polymorphic =\> true has no effect.  
You can use belongs\_to :foobar, :polymorphic =\> true or simply delete  
the line, it does not change anything.”

Josh S. – 2006-04-24 08:22:22:  
“@FranÃƒÂ§ois: Wow, that’s odd. But some of that association code is  
pretty bizarre.”

So really the trouble is that the target table uses STI and the School  
model wants a member\_type = “School” in the memberships table in  
addition to having type = “SchoolMembership” - but the User model only  
knows to put: type = “SchoolMembership.”  
It would be amazing if School would just look for type =  
“SchoolMembership” instead of both which is redundant.  
I thought there might be some option that I could add in the school  
class to get it to do that.

I could make a before\_save filter or something like that to make it  
work but if I go down that road I feel like I may as well go back to  
the messier/less-DRY stuff I had before and not re-write my model  
files with the more elegant (but flawed) new solution.

Thanks a lot for your reply,  
Much appreciated,  
All the best,  
Joseph

---

<div class="post-metadata">

**Author:** ![makkura](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/m/76d3ee/32.png) [@makkura](https://www.ruby-forum.com/u/makkura)\
**Post date:** [May 8, 2008, 11:21pm UTC](https://www.ruby-forum.com/t/has-many-through-polymorphic-with-sti-in-the-polymorphic-target-tables/137002/4 "2008-05-08T23:21:24Z")

</div>

Joseph,

okay, in the nicest possible way, and bearing in mind that all I have  
to go on is the information you presented about your code… you’re  
doing it wrong.

You quoted: [has\_many :through - The other side of polymorphic :through associations](http://blog.hasmanythrough.com/2006/4/3/polymorphic-through)

I wrote the patch that added the :source\_type option to Rails in  
response to that post.

So… a) the post is out of date - I fixed what he was talking about,  
and b) I really do know what I’m talking about here.

The reason adding :polymorphic =\> true makes no difference is that  
you’ve written your code to ignore it (this is why I gave you full  
examples).

You have:

class User \< ActiveRecord::Base  
has\_many :school\_memberships  
has\_many :schools, :through =\> :school\_memberships  
end

class Membership \< ActiveRecord::Base  
belongs\_to :user  
end

class SchoolMembership \< Membership  
belongs\_to :school, :foreign\_key =\> :member\_id  
belongs\_to :user  
end

And you’ve said that “a\_user.schools \<\< a\_school” doesn’t fill in  
member\_type.

Of course it doesn’t. Instead it’s doing exactly what you told it to do:

- create a SchoolMembership
- assign a\_user to school\_membership.user  
\*\* which assigns a\_user.id to school\_membership.user\_id
- assign a\_school to school\_membership.school  
\*\* which assigns a\_school.id to school\_membership.membership\_id

Go back and look at the code I sent you. With my code, this is what  
“a\_user.schools \<\< a\_school” does:

- create a Membership
- assign a\_user to membership.user  
\*\* which assigns a\_user.id to membership.user\_id
- assign a\_school to membership.member  
\*\* which assigns a\_school.id to membership.member\_id  
\*\* and which assigns a\_school.class.name to membership.member\_type

Regards,  
Trevor

On 5/8/08, Joseph S. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> :polymorphic =\> true part.  
> :polymorphic =\> true has no effect.  
> addition to having type = “SchoolMembership” - but the User model only
> 
> > Okay… first of all, if you don’t have a _darn_ good reason for using
> > 
> > has\_many :fluffy\_kittens, :through =\> :memberships, :source =\>  
> > has\_many :users, :through =\> :memberships  
> > do your bidding. But you’d be fighting the framework and it would be
> > 
> > > handling the polymorphism of the table. If I add this column in the  
> > > and  
> > > a\_school.users \<\< a\_user  
> > > a\_user.schools \<\< a\_school  
> > > end
> > > 
> > > # type :string
> > > 
> > > # id :integer
> > 
> > –  
> > Trevor S.[http://somethinglearned.com](http://somethinglearned.com)
> 
> >

## –

Trevor S.  
[http://somethinglearned.com](http://somethinglearned.com)

---

<div class="post-metadata">

**Author:** ![makkura](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/m/76d3ee/32.png) [@makkura](https://www.ruby-forum.com/u/makkura)\
**Post date:** [May 9, 2008, 3:17am UTC](https://www.ruby-forum.com/t/has-many-through-polymorphic-with-sti-in-the-polymorphic-target-tables/137002/5 "2008-05-09T03:17:27Z")

</div>

Hi Trevor,  
Thanks a lot for the clarification. I think I have it now.  
The :source\_type was the piece that I wasn’t getting.  
Indeed I was a bit worried about the 2 year old aspect of those blog  
entries.

Before I went over the edge with the SchoolMembership class we had:

class User \< ActiveRecord::Base  
has\_many :memberships  
has\_many :schools, :through =\> :memberships, :conditions =\>  
“memberships.member\_type = ‘College’ OR memberships.member\_of\_type =  
‘HighSchool’”  
end

That was not pretty and we couldn’t use built in methods, etc, so it  
felt wrong.  
Your active record patch is awesome and thank you for getting me back  
on track.  
All the best,  
Joseph
