Polymorphic STI with associations

I have been working on a polymorphic STI schema where the inheriting
classes have associations:

class Asset < ActiveRecord::Base
end

class Sound < Asset
belongs_to :user, :polymorphic => true
end

class User < ActiveRecord::Base
has_many :sounds
end

The Asset class contains a ‘type’ attribute which is getting properly
populated with the inheriting class’s name, so that’s fine, but I was
getting an error that there was no ‘user_type’:

undefined method `user_type’ for #Sound:0x4cf3740

OK, so I added ‘user_type’ to the Asset model and I can get past that
error (which was only raised upon a :destroy for the object). However,
now that I’ve added it, the user_type is not being populated when new
objects are created/saved. Am I missing something?

-eric

On Sep 16, 1:52 pm, Eric [email protected] wrote:

class User < ActiveRecord::Base
error (which was only raised upon a :destroy for the object). However,
now that I’ve added it, the user_type is not being populated when new
objects are created/saved. Am I missing something?

The :polymorphic and the user_type field aren’t necessary -
polymorphic associations are used when you’ve got objects from
multiple tables that all might be associated to a record, eg:

class Foo < AR::Base
belongs_to :things, :polymorphic => true
end

class ThingA < AR::Base
has_many :foos, :as => :thing
end

class ThingB < AR::Base
has_many :foos, :as => :thing
end

Note that ThingA and ThingB are in seperate tables - if they were
related via STI the :polymorphic wouldn’t be needed.

–Matt J.

On Sep 17, 8:44 am, Matt J. [email protected] wrote:

end

belongs_to :things, :polymorphic => true
Note that ThingA and ThingB are in seperate tables - if they were
related via STI the :polymorphic wouldn’t be needed.

Thanks for the reply, and I did leave a part out of my schema. There
will be other models inheriting from Asset, such that ultimately there
will be (at least):

class Asset < ActiveRecord::Base
end

class Sound < Asset
belongs_to :user, :polymorphic => true
end

class Video < Asset
belongs_to :user, :polymorphic => true
end

class Image < Asset
belongs_to :user, :polymorphic => true
end

class User < ActiveRecord::Base
has_many [:sounds,:videos,:images]
end

The idea will be to have @user.videos, @user.images, etc., so the STI
will handle the media types, and the polymorphism is used for User’s
relationship to each. However, currently user_id is not being saved
through the association, and even if I manually set user_id in Asset
(and user_type, just for fun), Video.first.user comes up nil. So, STI
is working as expected, but the association is broken in some way that
leads me to wonder whether Asset needs some connection with User
(unlikely, given the docs and commentary I’ve been able to find), or
whether the STI classes need some more connections to the base class
in order for User to pass through the STI.

-eric

On Sep 17, 11:10 am, Eric [email protected] wrote:

end
populated with the inheriting class’s name, so that’s fine, but I was
polymorphic associations are used when you’ve got objects from
class ThingB < AR::Base
class Asset < ActiveRecord::Base
class Image < Asset
through the association, and even if I manually set user_id in Asset
(and user_type, just for fun), Video.first.user comes up nil. So, STI
is working as expected, but the association is broken in some way that
leads me to wonder whether Asset needs some connection with User
(unlikely, given the docs and commentary I’ve been able to find), or
whether the STI classes need some more connections to the base class
in order for User to pass through the STI.

-eric

OK, so setting user_type to “User” manually in the base class does
seem to work. I’m not sure why I couldn’t find the user from (e.g.)
@sound before, but that’s water under the bridge now. The problem now
seems to be that even though Sound[,Video,etc.] are polymorphic to
User, the user_type attribute in the base class is not getting set
when saved through an inheriting class. Why could this be? Is there
some kind of accepts_nested_attributes_for catch that I’m not
accounting for?

Alternatively, is there a way to see the mechanism by which the type
field is set? More explicit logging, perhaps?

-eric

As my knowledge, it should be :

class Asset < ActiveRecord::Base
belongs_to :userable, :polymorphic => true

def userable_type=(sType)
super(sType.to_s.classify.constantize.base_class.to_s)
end
end

class Sound < Asset
end

class User < ActiveRecord::Base
has_many :assets , :as=>:userable
end

and in your assets table you must have 2 columns : userable_id and
userable_type

best regards

On Sep 17, 2:10 pm, Eric [email protected] wrote:

end
in order for User to pass through the STI.

Not sure what the issue you’re experiencing is - I’ll have to mock
some things up and try it. But I’ll reiterate that the :polymorphic is
not needed on those belongs_to declarations. The record that they
point to (a User) is always in the same table. You wouldn’t even need
it going the other way (some kind of User belongs_to :media, for
instance).

Oh, and that has_many isn’t (AFAIK) valid syntax…

–Matt J.

no, it still work event your class is inherited more than one level

On Sep 19, 7:33 am, robdoan [email protected] wrote:

As my knowledge, it should be :

[snip]

def userable_type=(sType)
super(sType.to_s.classify.constantize.base_class.to_s)
end
end

Um, no. If you’re needing to do this, you’ve done something seriously
wrong. This is already supported by :polymorphic => true. Also note
that your “solution” here will fail if the class associated to the
table (Asset in this case) is more than one level above the class
being stored (ie Asset > Sound > Mp3 will try to find records in a
‘sounds’ table => FAIL)

–Matt J.