Blog

Introduction to Database Design (on Rails): Part II

In a previous post, I explained the fundamentals of database design. This guide will cover the second half of that topic: how to make the database work with Rails. Before reading this you should have a good understanding of what a database is and how to organize one.

Rails is a framework that sits on top of the programming language Ruby. The framework speeds up web development by filling in code that you'd otherwise write from scratch on every new project. A component of this is ActiveRecord, a subset of Rails that acts as the bridge between your database and your Ruby code.

Here are two examples of a Ruby class:

Ruby classes

A class is a just a blueprint for a given object. Just like a house is made from blueprints, an author object is made from an author class. And just like a database table describes one thing, so does a class.

We represent relationships between tables like this:

Rails diagram

The top diagram should be familiar to you; it' s a simple foreign key/primary key database relationship. The Ruby classes at the bottom show how we represent this diagram in code. Here, we're saying that an author object will be the parent of many books, while a book object will have only one parent author.

Hint: it's often difficult to decide which class gets the :hasmany and which gets the :belongsto. The way you decide is by finding where the foreign key lives (in this example, the foreign key is authorid). The table with the foreign key will always be the :belongsto.

This works for all types of relationships. You can add multiple foreign keys to a table and simply add another :belongs_to on your Ruby class:

You can relate tables that don't have a direct connection (like authors and stores) through a third table:

So, what's the point of defining these relationships? The main benefit is that Rails gives you a number of helpful methods to grab records quickly and efficiently. With these simple class definitions…

…I can do the following:

# find an author and see books by her
rowling = Author.find_by_last_name("Rowling")
rowling.books # ["Harry Potter", "Harry Potter 2"] (return values here are simplified for clarity)

# find a book and see who the author is
harry_potter = Book.find_by_title("Harry Potter")
harry_potter.author # "J.K. Rowling"

# add a book to an author
harry_potter_3 = Book.new(title: "Harry Potter 3")
rowling.books << harry_potter_3 # Ruby array push syntax 
rowling.books # ["Harry Potter", "Harry Potter 2", "Harry Potter 3"]

These simple methods will take care of querying the database, matching the foreign keys, and returning the correct records. Without the help of ActiveRecord, I would be responsible for writing this implementation code myself. Take the following example:

harry_potter = Book.find_by_title("Harry Potter")
harry_potter.author # "J.K. Rowling"

If I didn't define my relationships, I would have to find the id of the book "Harry Potter" in my database, find its author_id attribute, go to the authors table, find the author with that id, then return that object. But since we've defined our Rails relationships, Active Record can select the right table and match the foreign keys automatically.

That about covers the basics. There are of course many different relationships I didn't cover here. You can read the full RailsGuide to get a handle on more advanced concepts, or feel free to leave a comment saying what you'd like me to expand on in a future post.