Project Ramon

A learning journey from a Ruby noob perspective

Ruby on Rails: Model Associations

rails_association_header_img

One of my most challenging hurdles in learning rails over the past 18 months has been in understanding how to use objects from outside classes. I received some clarity in this respect that has cleared things up for me. The Rails Guides, describe this best:

rails_association_img1

Today we’ll cover the complete setup required (not including project setup), and show some illustrations. My intent is for anyone who reads this post to have an easier time understanding this than I have.

Step 1: Parent and Descendant Class Setup

Lets assume that we just created our new rails project. From here we can create two models like so:

Here we have two classes. The User class contains attributes that allow us to store and retrieve data from our future User objects. attr_accessible is just a rails method that specifies a white list of model attributes that can be set via mass-assignment. The second class called Media houses the actual file in an attribute called file and allows for the reading and writing of a few additional attributes dealing with future Media objects.

The first thing we should do at this point, after having both classes created by running rails generate model User first_name last_name city state zip in our terminal, is to select one of these classes to be the parent of the other. By the way, if you’ve noticed that I didn’t specify the data-type for any of the attributes I listed above, its because rails defaults to the type of string when creating attributes in the terminal. If I had added an attribute of address, my terminal command for adding an address attribute would have looked like address:text. You can, if you’d like, continue to add string data-types to your attributes from the command line, it may help a very new developer have a slight advantage in understanding whats going on at the step of class creation from the terminal.

Step 2: Adding has_many and belongs_to

The next step towards passing objects between Rails classes or models is to name one of the associated classes the parent and the other a descendant. It makes sense to me that an object instantiated from the User class should have many medias. Usually (I can’t think of any edge cases, but I’m sure they exist) a media file is uploaded and edited by someone. So in this case we’ll assume that future objects of the class Media are being managed by our future User objects.

We could say it like this, a User has_many :medias. We use the plural form of the media because its assumed that each user will have the ability to manage multiple Media objects.
And since our User class includes a has_many :medias we place the line belongs_to :user in our Media. Lets see that get updated in our file of our two classes:

One thing to note about using a has_many and a belongs_to in our classes, is that the class that receives the belongs_to will always contain the foreign_key of it’s parent. So in this case, we need to add user_id to our Media class. This way whenever we write an Active Record query for a media object, we will automatically know to which user the media object belongs to. Here’s an example from one of my projects of the schema.rb file with a foreign key inside a class’ table with a belongs_to associated with it, I’ll keep it to two tables for brevity.

rails_association_img2

If we look at the articles table we can see that there is not an t.integer "id" column. This is because Rails builds in the "id" upon migration, and is not needed by the schema.rb file. However, as we can see from the above illustration, the Article class does have an association defined in it. That association has to be a belongs_to :event relationship, because a foreign key has been added to this table. Remember how I said that Rails will bake in the class’ "id" field? Well when a foreign key is added, you can also tell its foreign because the class associated with it will need it’s id spelled out. Like you see in articles, there is an event_id field. That may be a bit too much redundancy to explain the point, but its in the spirit of clarity.

The second table was shown just to briefly mention that I have two foreign keys (albeit they are from the same class). There are various methods available for the belongs_to association I’ll attempt to illustrate what the Rails Guides says about this:

Frist for the has_many :articles, dependent: :destroy bit.

rails_association_img3

Here’s an explanation about a method to use when you desire a custom foreign key name:

rails_association_img4

And finally an excerpt straight from the Rails guides about specifiying two foreign keys from the same class with different names:

rails_association_img5

Step 3: Time for Action!

Now that we have the setup out of the way. Lest see how we can pass an object from User to Media.

First, here’s our model setup:

Note that I’ve added a dependent: :destroy hash to the User‘s has_many :medias association. This enables database to keep clean. When a user decides to quit our application, not only are their attribute records removed, but due to our adding dependent: :destroy the @user object’s associated media files will be removed as well. Making all media objects associated with a specific user dependent upon that user for their existence.

To get this data into our views, we first have to package our desired object(s) in our controller, so it can pull the correct object’s data from the database through Active Record. Here’s a quick illustration of a part of my media controller from a project I’m currently building.

rails_association_img6

If you’re interested in learning more about the before_filter on line 2 in the above illustration, you should checkout my past post covering my first refactor: with Adam Cuppy.

So as we can see from my index method, I have assigned the parent class or model with the has_many :medias relationship expressed to an object that returns a list of all @media objects. To create a new @media object instead of @media = media.new we can use the build method instead.

And in any of our Media view files, we can use the @media instance variable to render data based upon a specific media object’s ID (for instance, in our show view file) or use @medias which will return all media objects in existence.

I wanted to thank my pairing pal Russell Baker, for helping me clear up a few things about Rails associations. Its always good to run problems by an actual person, especially if you’ve done the appropriate research, and find yourself stumped.

Advertisements

Categories: AirPair, Newbie, Ruby on rails

Tags: , , ,

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s