How to model these relations in Rails
For a couple of days now i am going back and forth how to models this.
I currently have the following model structure and relations (simplified)
class Fleet < ApplicationRecord # properties id, created_at, updated_at # A fleet should only consists of the same type, either a fleet or cars or a fleet of trains. Never both has_many :cars has_many :trains end class Car < ApplicationRecord # properties id, created_at, updated_at, number_of_gears, is_sports_car belongs_to :fleet end class Train < ApplicationRecord # properties id, created_at, updated_at, number_of_wagons, people_capacity, has_first_calass_section belongs_to :fleet en
The problem i have is thata fleet should only consists of the same type, either a fleet of cars or a fleet of trains. Never both.
With the current relations on fleet, it seems error prone to have both has_many associations (or more coming like Airplanes) in terms of integrity and ease of use when calling @fleet._cars-or-trains, i would have to know what i can call.
STI is not an option since the properties are very different on a Car and a Train. There are many more properties, for the sake of simplicity i have shortened them in this example.
What is the right way in Rails to do this?
Have you looked at Multiple Table Inheritance? Seems like it might work.
This gem makes it really easy: https://github.com/krautcomputing/active_record-acts_as
I would probably just add a
fleet_type column to the fleet that keeps track of which is allowed:
Car, etc. Then you can use it for validation against the associated models to make sure they're of the correct type.
is this something Rails has built in or would i need to set the fleet_type manually by checking if a Car or Train was created?
Not built-in. You could set it before_create and check the first child model type and use that, and also validate that they're all the same type.
Here's some quick psuedo code:
class Fleet has_many :cars has_many :trucks validate :same_type before_create do self.fleet_type = "Car" if cars.exists? self.fleet_type = "Truck" if trucks.exists? end private def same_type if cars.exists? && trucks.exists? errors.add(:base, "All items in fleet must be of the same type") end end end