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?
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.
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.
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?
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...
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)