I have a class, call it 'School'.
An instance of School can have all sorts of assets, such as 'Books',
'Tables', 'Chairs', 'Teachers'.
Each of these asset types is modeled by it's own model and has a
corresponding table in the db. So, there is a table for 'Chair,
another for 'Teacher' and so on.
In summary, in the abstract, each school has_many assets and each
asset belongs_to a school.
I'd like to be able to do things like:
school = School.find(3)
school.assets << Chair.new(chair_params)
school.assets << Teacher.new(teacher_params)
school.assets.each{|a| puts a.name}
I tried to solve it by defining an abstract class:
class Asset << ActiveRecord::Base
self.abstract_class = true
end
and inheriting from it:
class Book << Assets
belongs_to :school
end
and
class School << ActiveRecord::Base
has_many :assets
end
That gets me part way there but I fear is fundamentally flawed. Most
of the time it results in complaints from raisl that the table
'Assets' doesn't exist, which is true...
Any suggestions on how to model the relationship I described would be
much appreciated!
I'm staying away from STI because I want to be able to add additional
asset types over time, none of which share attributes with other
assets. So - with STI, I could end up with a table that has hundreds
of columns.
Seems like this is just a reverse of a normal polymorphic association. A tricky problem to solve, since rails doesn’t seem to be designed to handle it.
When you call “school_instance.assets” the underlying query would essentially need to be “give me everything from the books table, teachers table, and chairs table”. That’s basically impossible if you understand the SQL behind it, since each of these tables has a different schema. Single Table Inheritance solves that issue.
It is possible to work around it if you want 3 separate tables…
If an asset can belong to only one school, it’s probably easiest to do away with the abstract parent class and just have them each “belong_to :school”
Then in your School class, you can add a method to retrieve all the associated assets:
And this is a problem because...? Sounds an awful lot like premature
optimization.
This sort of thing is not supported well by Rails, as it's tricky to
handle in general (after all, one could create additional subclasses
of Asset on-the-fly so Rails can't be sure which tables it should be
querying) and even if possible wouldn't support some operations
cleanly - imagine trying to paginate the assets association...
One thought was using a join model with a polymorphic belongs_to and
has_many :through, but that won't work either:
One thing that *might* work would be to have a model that holds the
common attributes that then belongs_to the specific types. Not
optimal, but might work with some usage patterns.
I'm assuming the models you're using are generic examples and not the
real models, so I'll skip addressing the fact that there are very few
operations / actions that make sense applied to both 'Chair' and
'Teacher'. Even including both in a single list would seem pretty
peculiar.
I ended up solving this much along the lines that Tim recommended -
lots of 'has_many' statements in the School model and several custom
built accessors such as those that Tim described.