Multiple Models and Relationships
Mr. Blackford, the store owner, has been happily storing products in the simple terminal application you set up. However, as he adds more products to the store, it becomes harder to browse through them to find a specific one. It would be great if there was a way to organize the products into categories.
Let's create a new model called Category and then figure out how to use it organize the products. For now Category will just have one custom column for its name, so let's generate it by entering the following command in the terminal:
rails generate model Category name:text
Note: if the Rails console is still running, exit it with Ctrl-C before entering the above command.
Then migrate it to setup the table:
rake db:migrate
Output:
== 20150324231527 CreateCategories: migrating ================================= -- create_table(:categories) -> 0.0012s == 20150324231527 CreateCategories: migrated (0.0013s) ========================
Database
Databases are powerful because they let you connect different tables of data with each other. For example, we can easily classify products into categories.
Q: How can we classify the products into categories?
A: We need to identify each product with the category it belongs to. Let's add a new column category_id
to products to mark the category of each product.
Q: How can we get a list of all products in a specific category?
A: Simple. We just ask the computer to go through all the products and return the ones that belong to the category we want. For example, if we wanted the products in category 1, the computer would go through the database and return the products where the category_id
is 1.
Q: Will we need to write SQL to get this information?
A: No, Rails makes it easy for us. We just need to setup the columns and tell Rails about them.
Adding Columns
Let's go ahead and add a new column to Products. Whenever we want to change a database in a Rails, we use a migration file. Earlier, we used a migration to create a new table for our model, but now we just need to add a new column. Enter the following code into your terminal:
rails generate migration AddCategoryIdToProducts category_id:integer
This will create a migration for adding a category_id column to products. Here's the general format for creating a migration to add column_name
to TableName
:
rails generate migration AddNameToTableName column_name:type
The type
specifies what data will be stored in the column, such as text or integer data. Once you've created the migration file, run rake db:migrate
to add the column to the database.
== 20150325004821 AddCategoryIdToProducts: migrating ========================== -- add_column(:products, :category_id, :integer) -> 0.0456s == 20150325004821 AddCategoryIdToProducts: migrated (0.0458s) =================
Rails Associations
Now that the database is set up, we need to tell Rails how to use this information. This is done with an association, which associates one model with another.
Fire up your rails console and enter the following
prod = Product.find(1)
. This will return the first product you created earlier.prod.category_id
This will return nil.- Let's create a new category:
Category.create(name: "Animals")
- Set the id of
prod
:
prod.category_id = 1
prod.save
belongs_to
Now that out product has an assigned category_id, it would convenient if we could just enter prod.category
to get its category. However that doesn't work right now, since we didn't set up an association. We need to tell Rails that Product belongs_to
category. Modify your Product class in Rails as follows:
class Product < ActiveRecord::Base
belongs_to :category
end
Now reload your terminal and enter prod = Product.find(1)
again. Then enter prod.category
and it will return the category!
has_many
We can get the category of a product, but it would be even more convenient if we could do the reverse and get all the products that belong_to a category. Luckily we can do that in Rails by declaring that Category has_many
products. Modify your Category class in Rails as follows:
class Category < ActiveRecord::Base
has_many :products
end
This is a common pattern of model associations. One model, such as a User or Category, has_many
related items of another model, such as Products, Comments or Items. We add an 'parent' id column to the "Many" model (such as Items) to associate it with the "Single" model (such as User). We then tell Rails that User has_many :items
and Item belongs_to :user
.
Console Time
Now that we've set everything up in the application, restart your rails console and create two more categories:
category1 = Category.create(name: "Foods")
Category.create(name: "Machines")
Let's create two new products and assign them to the category we just created:
Product.create(name: "Tomato", description: "red", price: 2, category_id: category1.id)
Product.create(name: "Bread", description: "whole wheat", price: 3, category_id: category1.id)
You can now get any category and easily list out all its products. For example:
category1.products
This will return all the products in category1
. Let's assign what it returns to a variable:
products = category1.products
Challenge
Can you now go through the products
collection and print out the name of all its products? You can do this in the challenge below and in your console.
To get the name of a product, simply call .name
on it.
Guideline: See Each blocks for the ruby way to iterate through a collection of items.
Mr. Blackford is happy with the new tool you set up to organize his products in the console. In the future, you'll be able to use this categorization to let people browser a website by category. Meanwhile Mr. Blackford is wondering how the application will validate data...
Challenge
You would like to let users make many comments on your site. Consider what column to add to Comments to associate them with User. Which code below will correctly generate a migration file to add this column?
Please sign in or sign up to submit answers.
Alternatively, you can try out Learneroo before signing up.
Challenge
An app already has User, Comments and a user_id column in comments. What line of code should you add to the User model to tell rails that each User has many Comments?
Please sign in or sign up to submit answers.
Alternatively, you can try out Learneroo before signing up.
Challenge
Print out the name of each product in products
. Print each name on its own line.
Please sign in or sign up to submit answers.
Alternatively, you can try out Learneroo before signing up.