Rails from first principles: Building a has_many bi-directional association
A great way to learn is by trying to explain, so as part of my rails association learning process, I’ll attempt to explain a slightly more complex bi-directional association between two models. This is part of the “Building advanced forms” exercise of The Odin Project.
Objective: I want to build a proof of concept flight-booking app, and the first requirement is to create associations between an Airport model and a Flight model.
Steps:
- Each airport will have many flights, and each flight will belong to an airport. Airports have 3-letter international codes, e.g. LIS for the Lisbon international airport.
- Each flight, however, will need both origin and destination airports.
- Each airport will have departing and arriving flights.
So what does this mean for our model associations?
Step 1:
The airport model has a Code variable, which is a string field, such as “SFO” or “LIS”. This association creates the Airport.find(1).flight
and Flight.find(1).airport
methods which let you query Airport flights and flight airports, respectively.
Step 2:
Now we need to separate flight airports into origin airports and destination airports, since flights fly from A to B. We will call these from_airport
and to_airport
to make the relationships easier to understand. The Flight associations are changed to:
This means that each flight will have a from_airport and a to_airport, but that these are simply different instances of the Airport model. This means we have to create a from_airport_id
field and a to_airport_id
field in the Flight model in order to associate each Flight to two airports, the origin and destination ones.
Step 3:
The next step is to add departures and arrivals to the airport model. A departing flight of an airport is a flight whose from_airport field corresponds to the airport id of that airport. An arriving flight is a flight whose to_airport field corresponds to the airport’s id.
So what we want to do is to call these flights departing_flights
and arriving_flights
, and associate them to a from_airport_id
field and a to_airport_id
field, in the Flight model. This association is done by setting a foreign_key
.
In narrative form, this means that Airports have flights, and they are either departing or arriving depending on whether the from_airport_id
or to_airport_id
field corresponds to the airport ID.
Likewise, Flights have “from airports” (origins) and “to airports” (destinations), which is set by the ids in from_airport_id
and to_airport_id
.
We can now use methods such as Airport.first.departing_flights
to get the departing flights of the first airport, and Flight.first.from_airport
to get the origin of the first flight.
If we wanted to, we could change from_airport
to origin
and to_airport
to destination
but we’ll leave it as it is in the interest of simplicity. We could also add inverse_of
to make database querying more efficient, but we’ll also leave that out as it’s not a strict requirement.
And that’s it. Our app now has Airports with many departing and arriving flights, as well as Flights with origins and destinations.
— — —
Hope this was insightful. Feedback is always welcome.