Rails: ActiveRecord Relational Objects
One of the first analogies described to me about programming compared it to the likes of solving a puzzle. If you already understand the inner workings of building software or web applications, then you might agree with that sentiment. If you’re still trying to make sense of it all, you may be thinking, ’Well, what kind of puzzle?’ At first glance, it kind of resembles a crossword. Syntactically placed characters lined up in rows. Then one day, you find yourself knee deep in code and it starts to feel more like a word search. Scavenging for bug fixes caused by incorrect method calls and grammatical errors. If you still haven’t the slightest indication as to what I’m referring to, my guess is that programming looks more like a Rubik’s Cube and you can’t seem to make any which way of it. Alas, I’ve come forth today baring response to such a puzzling question. The answer is Yes.
Recollect that ActiveRecord is an Object Relational Mapper (ORM) — specific to Ruby (DSL)— defined by the documentation as “a technique that connects the rich objects of an application to tables in a relational database management system.” A technique is an understatement. It is a multi-purpose back-end technology designed to handle any task related to an application’s database. It allows you to construct, persist, store, read and manipulate real world objects that interact with one another without having to write any SQL. You can shape these objects to fit together, sort of like a Jigsaw puzzle.
Models are the pieces to the Jigsaw. They are bound by ActiveRecord associations and are structured however you intend your data to flow in conjunction. After migrating a model’s table to the database, ActiveRecord maps the columns of each row with the attributes that make up said model. This is essentially how a relational database operates.
NOTE: Rails, as a whole, is made up of some very cool technology. I’d like to point out that the concepts being discussed are not specific to only Ruby on Rails. Programming Languages [Ruby, JavaScript, Python] and their respective frameworks [Rails, React, Django] generally share the same ideologies with the practice of CRUD. It’s essentially the how-to that differentiates each one.
ActiveRecord: Associations
ActiveRecord Associations, at their simplest form, are responsible for the flow of your data. In terms of data flow — appropriately — visualize a railroad, with the associations being what connects the tracks. While the train is your data, stored inside of each compartment.
In regards to our puzzle quandary…if a model is one single piece to the Jigsaw, in order to connect (associate) multiple models together, you’re presented with a number of Riddles to solve. The answers to this paradox can be found within association types.
Association types define an ActiveRecord relationship between two or more models. Each type associates a specific relationship that connects models together. Understanding how every relationship communicates will determine how efficiently you pass data throughout your Application. There are also association types that seem similar but are subtly different.
NOTE: it is good practice to write down your Associations during the planning phase of an Application’s Development Life Cycle. It can be demoralizing to spend valuable time building only to realize that your models aren’t connected properly or to the specifications provided by your Project Manager. On the other hand, it can be an invaluable learning experience having to endeavor the task of recreating tables and relationships on the fly. Each case is situational and unique, tread lightly…
You know that relationships are formed by using association types. But you may be wondering…how does it work?
To that, I’d answer, “Rails Magic”.
And you’d counter with…Well, what’s Rails Magic?
Or worse…You believe in Magic!?
Then it would turn into a thing…
The easy way out is to tell you the “magic” lies inside of abstraction. A summed up overview would explain how — in some cases but not limited to this example — Rails will perform an implicit call on a class so that you don’t have to write extra syntax, which DRY’s up your code. It just knows…
Rails provides macros that handle a wide range of abstracted functionality…
1. the #before_action macro performs a callback task. Here, it will execute #find_user when a client makes an http ‘get’ request to the User show page.
2. #find_user will then check for an :id which associates that instance to the User table.
3. when an instance of the User class is found, the #show action implicitly redirects to #user_path(@user.id), displaying the User show page.
NOTE: the “magic” behind Rails has to do with the functionality happening under the hood. It’s just a bunch of Ruby code that takes place somewhere else within the framework that you can access with a macro. If followed correctly, the mantra, “Convention over Configuration,” will make sure everything runs accordingly.
How did we get here?!
Remember those association types that I mentioned? They are Rails macros, just like the #before_action in the example above.
Relationships:
- belongs_to
- has_one
- has_many
- has_many, through:
- has_one, through:
- has_and_belongs_to_many
- many-to-many
- polymorphic associations *
* a Polymorphic Association is an advanced relationship. It essentially means that one instance of a model can belong to multiple models by way of a single association.
has_many / belongs_to
I find the most common relationship to be the has_many/belongs_to. An understanding of this association will put you in a position to work with almost any type.
Riddle:
- one instance of a model has multiple instances of another model.
- an instance of a model belongs to a single instance of another model.
NOTE: a new object of a model is referred to as an instance.
has_many
An artist has many songs
One instance of an @artist has multiple instances of @songs in their catalog.
An instance of this model will have access to an array of objects containing multiple instances of the model belonging to it.
belongs_to
A song belongs to an artist
An instance of this model will have access to a single instance of the relational object it belongs to by storing its foreign key when instantiated.
The data — from the associated object it belongs to — is stored as an attribute— on a column — inside of the database table — which holds the value of the associated object’s :id — in the form of an integer — known as a foreign key.
Each instance of a @song tracks the @artist it belongs to by its foreign key.
has_many, through
This is easily my favorite association at the moment. You can do so much with it. I will caution that it would be best to conceptually understand the has_many/belongs_to relationship first before moving ahead.
Riddle:
- a race has many characters && has many karts through characters
- a character belongs to a race && a character belongs to a kart
- a kart has many characters && has many races through characters
has_many, through
At its core, a has_many, through relationship associates three models together. Two of those models will both ‘have’ many instances of the third model, while the third model —which belongs to each of the other two— creates what is referred to as a join table.
Join Table
A model that joins two or more objects by storing their foreign keys on its table in the database.
Now…this is where the “magic” happens!
When creating an instance of a join table model, Rails will implicitly associate both foreign keys in the table by way of the has_many, through macro.
NOTE: Convention over Configuration.
Once a join is created, it associates the two models by storing each foreign key on the table, allowing both access to multiple instances of each other.
NOTE: Don’t count on it looking that organized when it displays in your terminal. That’s just for show. Testing everything in your terminal is your best bet to make sure everything is associated properly.
Summary
You have to go a lot deeper to fully understand how convenient associating models can be. This is barely scratching the surface. I intend to go further and show what else relating objects allows you to do. There are plenty more puzzles to be solved. The real fun begins when you start creating associated models through nested forms or writing custom scope methods to specify a query search. For now, let’s recap:
- ActiveRecord is an ORM — Object Relational Mapper — written specifically for Ruby connects your application’s relational objects to the tables in a relational database.
- Models are linked together using association types that form relationships between the objects created by your application.
- Association types are Rails macros that offer a variety of functionality which takes place under the hood of the framework.
- Rails “magic” is nothing more than abstracted code accessed by Rails macros.
I wrote this post upon completion of my Rails project while immersed in the Flatiron School curriculum.