A Deeper Look Into Active Record Associations and Their Methods
Published: 2020-12-03 21:59:16

In my last post, I talked about my second project for Flatiron's software engineering course. I built a web application that allows a user to track the condition of their skin and log any comments in regard to how it feels on any particular day. Using SQLite as a database, the user is also able to view any past logs and make any changes if necessary. I glossed over it in my previous post, but I wanted to take a deeper look into using Active Record. Specifically, how we can establish relationships using Active Record between our models and the various new methods that we gain from it. 

Active Record gives us a few different options with the type of relationship we establish between our models. 

```belongs_to
   has_one
   has_many
   has_many :through
   has_one :through
   has_and_belongs_to_many
```
In my case, I had a model and a table for a user, and another set for my logs. I used a one to many relationship between the two. A user `has_many` logs, while each log `belongs_to` a designated user. To set up the relationship, I started by adding a `user_id` column in my logs table, because the logs belong to a user, we set the foreign key in the table of objects that belongs to another model. I then just had to add Active Record's macros into the models. 
```
class User < ActiveRecord::Base 
      has_many :logs
end

class Log < ActiveRecord::Base
      belongs_to :user
end 
```
And just like that, the association is set between the user and the logs they create!  Active Record also gives us a lot of very cool methods to use with our models. They vary depending on what association is used. Since I used the belongs_to/has_many association in my project, I'm going to elaborate a little more on the methods we gain from the use of these macros. 

The belongs_to association creates a 1-1 relationship between two different models. So, each log can only have one user or one creator. This association gives us six methods that we can use with our models.

```
   association
   association=(associate)
   build_association(attributes = {})
   create_association(attributes = {})
   create_association!(attributes = {})
   reload_association
```
I'll demonstrate a few of these methods through the context of my project and it's models. In these methods, just replace the word "association for the model passed into the argument of `belongs_to`. In my case, a 'log' `belongs_to :user`. Each instance of the 'log' model will have access to these new methods.

```
   user
   user=
   build_user
   create_user
   create_user!
   reload_user
```
The `association` method just returns the associated object, if none can be found, it just returns nil. If the object associated has already been retrieved from the database, we can use another one of our handy dandy methods to override this behavior, by calling `reload_user`.

Next, we have the `association=(associate)` method. In my case, it would look like this `user=(log)`. Here we can assign an object to be associated with this other. This basically means, we take the primary key from the associated object and set the other object's foreign key to the same number. So, if I had a log, that somehow wasn't associated to a particular user at creation, I could go in and manually do it with this method. 

Another, important one to know is the `build_association(attributes = {})`. It would look like this in the case of my project, `@log.build_user(:name => "Bessy", :email => "test@email.com"). This method basically allows us to create a new instance of a user that has the attributes I passed in. So, to recap, I basically created a new user that has a name and an email, all through that method alone. It's important to note that if you use this method, your object will NOT be saved. 

To create an instance and then save it, we can use the `create_association(attributes = {})`. Following the same format as previously, we simply use `@log.create_user(:name => "Bessy", :email => "test@email.com")`. It has to pass all of the validations specified by the associated model before it can be saved. 

The `create_association!(attributes = {})` or  `@log.create_user!(:name => "Bessy", :email => "test@email.com")` does virtually the same as the create_association method above, but the addition of the bang operator raises an error if the record is not valid. 

As I mentioned previously, the methods gained from Active Record, vary on which associative macro you used. The `has_many` association comes with its own methods that we'll explore as well. The `has_many` association, creates a one to many relationship. One object can have many things. We gain 17 new methods related to this association!!
 
```
collection
collection<<(object, ...)
collection.delete(object, ...)
collection.destroy(object, ...)
collection=(objects)
collection_singular_ids
collection_singular_ids=(ids)
collection.clear
collection.empty?
collection.size
collection.find(...)
collection.where(...)
collection.exists?(...)
collection.build(attributes = {}, ...)
collection.create(attributes = {})
collection.create!(attributes = {})
collection.reload
```
I obviously can't cover all seventeen methods, but I'll recap on the more common ones used. Like before, just replace `collection` with the symbol passed as the argument to the `has_many` macro. So because I used, `has_many :logs`, all instances of the User model will gain these methods. 

The `collection` method here, `@user.logs` will return all of the associated objects. If there are none, it returns an empty Relation.

`collection <<(object,..)` would add one or more objects to the collection by setting their foreign keys to the primary key of the calling model. So, if I had a user with a primary key of 25, I could then use the shovel operator to add new instances of logs and set their foreign key to 25, to associate those logs to user number 25. This method could look something like this `user.logs << new_log`. 

Similar to some of the methods gained from the `belongs_to` association, the `has_many` also gives us `collection.build` and `collection.create`. There are a few differences to note though. With the `has_many` association, these methods return a single array, or an array of new objects corresponding to the associated type. 

If we used, `@user.logs.build(:current_condition => "normal", :new_products => true, :comments => "Everything is normal!") The log will be instantiated with the attributes I passed into the build method, and the link through the key will be created, but it will NOT be saved!

On the other hand, if we used `@user.logs.create(:current_condition => "normal", :new_products => true, :comments => "Everything is normal!")`, we can just save that new instance of a log after it passes the validations. 

There are so many methods that Active Record gives us with the use of associations. I am barely scratching the surface here. If you're starting a rails project, I recommend you check out the documentation that goes along with it. Feel free to drop some comments, tips, or any questions. :)