ActiveRecord::SubclassNotFound error when using STI in Rails
I am using Ruby on Rails 2.3.10. I have a class, School
. I use STI to give me several subclasses: PrimarySchool
, SecondarySchool
and University
.
app/models/primary_school.rb:
class PrimarySchool < School
has_many :education_records, :foreign_key => "school_id"
end
app/models/secondary_school.rb:
class SecondarySchool < School
has_many :education_开发者_StackOverflow社区records, :foreign_key => "school_id"
end
app/models/university.rb
class University < School
has_and_belongs_to_many :applicants, :join_table => :applicants_schools, :foreign_key => "school_id"
end
db/migrate/20100824170203_create_schools.rb:
class CreateSchools < ActiveRecord::Migration
def self.up
create_table :schools do |t|
t.string :name
t.string :type # secondary, cegep, college, university
t.string :street
...
t.timestamps
end
end
def self.down
drop_table :schools
end
end
PrimarySchool.first
and PrimarySchool.last
work as expected. SecondarySchool.first
and SecondarySchool.last
work as expected. University.last
works.
However, University.first
and University.all
raise a ActiveRecord::SubclassNotFound
exception:
ActiveRecord::SubclassNotFound: The single-table inheritance mechanism failed to locate the subclass: 'University'. This error is raised because the column 'type' is reserved for storing the class in case of inheritance. Please rename this column if you didn't intend it to be used for storing the inheritance class or overwrite University.inheritance_column to use another column for that information.
from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.10/lib/active_record/base.rb:1671:in `instantiate'
from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.10/lib/active_record/base.rb:665:in `find_by_sql'
from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.10/lib/active_record/base.rb:665:in `collect!'
from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.10/lib/active_record/base.rb:665:in `find_by_sql'
from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.10/lib/active_record/base.rb:1582:in `find_every'
from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.10/lib/active_record/base.rb:619:in `find'
from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.10/lib/active_record/base.rb:639:in `all'
from (irb):6
from :0
The SQL generated is correct:
SecondarySchool Load (0.3ms) SELECT * FROM `schools` WHERE ( (`schools`.`type` = 'University' ) ) LIMIT 1
SecondarySchool Load (1.7ms) SELECT * FROM `schools` WHERE ( (`schools`.`type` = 'University' ) ) ORDER BY schools.id DESC LIMIT 1
What am I missing/doing wrong?
This is a known issue in the development mode.
One workaround is to register the subclasses at the bottom of the base class file.
%w(secondary_school university).each do |r|
require_dependency r
end if Rails.env.development?
KandadaBoggu put me on the right track. Pete P.'s workaround works for me:
class School < ActiveRecord::Base
def self.subclasses
[PrimarySchool, SecondarySchool, OtherSchool, University]
end
end
In that it gets rid of the exception...but it generates the wrong SQL:
SELECT * FROM `schools` WHERE ( (`schools`.`type` = 'University' OR
`schools`.`type` = 'PrimarySchool' OR `schools`.`type` = 'SecondarySchool' OR
`schools`.`type` = 'OtherSchool' OR `schools`.`type` = 'PostSecondaryInstitution' OR
`schools`.`type` = 'Cegep' OR `schools`.`type` = 'College' OR `schools`.`type` =
'University' ) ) LIMIT 1
Renaming university.rb to uni.rb, changing the class name and UPDATEing the type column made this work. I'm really confused.
精彩评论