Pascal Lindelauf
Pascal Lindelauf

Reputation: 4870

has_many with multi-level hierarchy and single table inheritance

In my Rails app I have a multi-level hierarchy of the following kind:

class Vehicle < ActiveRecord::Base end
class RoadVehicle < Vehicle end
class Car < RoadVehicle end
class Buss < RoadVehicle end

Then I have a class referencing the middle level like so:

class Garage < ActiveRecord::Base
  has_many :road_vehicles
end

In this simplified example, I have given the vehicles table a type column to enable single table inheritance. Additionally, it contains a garage_id column, to enable the has_many relationship. When I create a new garage and add cars and busses, all get added to the database as expected. However, when I later retrieve the garage object and inspect the road_vehicles collection, it is empty. Can anyone tell me what I'm doing wrong?

Upvotes: 6

Views: 2065

Answers (2)

Pascal Lindelauf
Pascal Lindelauf

Reputation: 4870

The actual correct answer to date is that Rails has no notion of all relevant subclasses when it does not eager load them on boot. The latter is often the case in development, where eager_load = true is set in development.rb.

Solutions to make this work are described in this Rails article. I chose to put the classes of the relevant class hierarchy into one directory and explicitly eager load them when eager_load is set to false.

From the documentation:

shapes = "#{Rails.root}/app/models/shapes"
Rails.autoloaders.main.collapse(shapes) # Not a namespace.

unless Rails.application.config.eager_load
  Rails.application.config.to_prepare do
    Rails.autoloaders.main.eager_load_dir(shapes)
  end
end

Upvotes: 0

Alex Reisner
Alex Reisner

Reputation: 29467

When setting up associations with single table inheritance models, you need to refer to the parent model so the associations can infer a table name. So, in your Garage class you need:

has_many :vehicles

If you want to restrict the association to RoadVehicles, you can add conditions:

has_many :vehicles, :conditions => {:type => ['Car', 'Bus']}

Upvotes: 6

Related Questions