I have a case of polymorphic association and STI here.
# app/models/car.rb
class Car < ActiveRecord::Base
belongs_to :borrowable, :polymorphic => true
end
# app/models/staff.rb
class Staff < ActiveRecord::Base
has_one :car, :as => :borrowable, :dependent => :destroy
end
# app/models/guard.rb
class Guard < Staff
end
In order for the polymorphic assocation to work, according to the API documentation on Polymorphic Assocation, http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#label-Polymorphic+Associations that I have to set borrowable_type
to the base_class
of STI models, that is in my case is Staff
.
The question is: Why doesn't it work if the borrowable_type
set to STI class?
Some test to prove it:
# now the test speaks only truth
# test/fixtures/cars.yml
one:
name: Enzo
borrowable: staff (Staff)
two:
name: Mustang
borrowable: guard (Guard)
# test/fixtures/staffs.yml
staff:
name: Jullia Gillard
guard:
name: Joni Bravo
type: Guard
# test/units/car_test.rb
require 'test_helper'
class CarTest < ActiveSupport::TestCase
setup do
@staff = staffs(:staff)
@guard = staffs(:guard)
end
test "should be destroyed if an associated staff is destroyed" do
assert_difference('Car.count', -1) do
@staff.destroy
end
end
test "should be destroyed if an associated guard is destroyed" do
assert_difference('Car.count', -1) do
@guard.destroy
end
end
end
But it seems to be true only with Staff instance. The results are:
# Running tests:
F.
Finished tests in 0.146657s, 13.6373 tests/s, 13.6373 assertions/s.
1) Failure:
test_should_be_destroyed_if_an_associated_guard_is_destroyed(CarTest) [/private/tmp/guineapig/test/unit/car_test.rb:16]:
"Car.count" didn't change by -1.
<1> expected but was
<2>.
Thanks
I agree with the general comments that this ought to be easier. That said, here is what worked for me.
I have a model with Firm as the base class and Customer and Prospect as the STI classes, as so:
I also have a polymorphic class, Opportunity, which looks like this:
I want to refer to opportunities as either
or
To do that I changed the models as follows.
I save opportunities with an opportunistic_type of 'Firm' (the base class) and the respective customer or prospect id as the opportunistic_id.
Now I can get customer.opportunities and prospect.opportunities exactly as I want.
An older question, but the issue in Rails 4 still remains. Another option is to dynamically create/overwrite the
_type
method with a concern. This would be useful if your app uses multiple polymorphic associations with STI and you want to keep the logic in one place.This concern will grab all polymorphic associations and ensure that the record is always saved using the base class.
Then just include it in your model:
There is a gem. https://github.com/appfolio/store_base_sti_class
Tested and it works on various versions of AR.
Just had this issue in
Rails 4.2
. I found two ways to resolve:--
The problem is that Rails uses the
base_class
name of the STI relationship.The reason for this has been documented in the other answers, but the gist is that the core team seem to feel that you should be able to reference the table rather than the class for a polymorphic STI association.
I disagree with this idea, but am not part of the Rails Core team, so don't have much input into resolving it.
There are two ways to fix it:
--
1) Insert at model-level:
This will change the
{x}_type
record before the creation of the data into the db. This works very well, and still retains the polymorphic nature of the association.2) Override Core
ActiveRecord
methodsA more systemic way to fix the issue is to edit the
ActiveRecord
core methods which govern it. I used references in this gem to find out which elements needed to be fixed / overridden.This is untested and still needs extensions for some of the other parts of the ActiveRecord core methods, but seems to work for my local system.
Good question. I had exactly the same problem using Rails 3.1. Looks like you can not do this, because it does not work. Probably it is an intended behavior. Apparently, using polymorphic associations in combination with Single Table Inheritance (STI) in Rails is a bit complicated.
The current Rails documentation for Rails 3.2 gives this advice for combining polymorphic associations and STI:
In your case the base model would be "Staff", i.e. "borrowable_type" should be "Staff" for all items, not "Guard". It is possible to make the derived class appear as the base class by using "becomes" :
guard.becomes(Staff)
. One could set the column "borrowable_type" directly to the base class "Staff", or as the Rails Documentation suggests, convert it automatically using