So, you've probably found yourself asking these types of questions:
- What exactly is SQL?
- How do Databases work?
- What are Rails Models doing behind the scene?
- What in the world is going on with my terminal's output???
Today's objective is to answer those questions and help you get a grip on how your Rails Models work with your database.
A relational database is a series of Tables with Columns and Rows... Yes... Kind of like Excel or CSV.
Let's take a look at a Relational-Databasey version of a Todo App:
| Name | Priority |
|---|---|
| Pay Electric Bill | 1 |
| Pick up Vegan Cheese | 3 |
| Send out Party Invitations | 2 |
In this situation, the Tasks table contains 2 columns: Name and Priority.
In OOP terms, these columns would be considered the attributes of an object. And each individual row would be considered an object of the Todo class.
So, when you create a new object by running the command Todo.new("call sister", 2)
You'll create the following in the database:
| Name | Priority |
|---|---|
| Pay Electric Bill | 1 |
| Pick up Vegan Cheese | 3 |
| Send out Party Invitations | 2 |
| call sister | 2 |
Now a Non-Relational version:
{
"task":
{
"task" : "Pay Electric Bill",
"priority" : "1"
}
}Explaining non-relational databases is outside of the scope of this particular lesson.
What you do need to know is that both styles of data storage (relational and non-relational) have strengths and weaknesses.
Rails primarily uses Relational Databases, and mastering SQL (along with Relational Database concepts) is essential to becoming a good Rails Developer.
Once you've mastered the Relational Database, understanding Non-Relational Databases will be a breeze.
- - - -
So what is SQL?
SQL is an ancronym. Yes, another acronymn...
that stands for:
S - Structured
Q - Query
L - Language
Great! so...
According to this site:
SQL (Structured Query Language) is a standard interactive and programming language for getting information from and updating a database... Queries take the form of a command language that lets you select, insert, update, find out the location of data, and so forth.
Basically... You submit queries, through the SQL language, to a structured (relational) database. Those queries can insert (create), select (read), update, or delete items.
You can directly map these actions to the standard Rails operations

note: SQL can do significantly more than basic CRUD actions, but that is outside of the scope of this lesson. We are mainly focusing on the standard SQL actions for web applications.
(challenges in this stage will be completeed by the instructor, unless you are working through the lesson independently)
Challenge 1
- Create a new rails app titled db_explore
- Navigate to the new app
- use scaffolding to generate a User Resource, with the following: 3. name (string) 4. age (integer) 5. city (string)
- Create the table in the database
- Once complete, Navitage to
localhost:3000/usersand add 5 users
Challenge 1 Answers:
$ rails new db_explore
$ cd db_explore$ rails g scaffold User name:string age:integer city:string$ rake db:migrateYou can actually jump into the database directly by running $ rails dbconsole
You'll be dropped into the database and should see a prompt that looks something like this: sqlite>
Start by running the .help command. This will give you an idea of what you can accomplish in the dbconsole.
SELECT is by far the most complicated part of SQL, so we'll start there.
You can pull all of the data from a specific table by running the following command:
sqlite> SELECT * FROM users;Which will return:
1|Shane|31|Charlotte|2015-11-30 04:48:49.839777|2015-11-30 04:50:12.119858
2|Zack|26|Mooresville|2015-11-30 04:49:32.560862|2015-11-30 04:49:32.560862
3|Jon Paul|30|Los Angeles |2015-11-30 04:49:56.959506|2015-11-30 04:49:56.959506
4|Nick|28|Kansas City|2015-11-30 04:51:17.870682|2015-11-30 04:51:17.870682
5|Emily|33|Charlotte|2015-11-30 04:54:17.030918|2015-11-30 04:54:17.030918Let's break down what's happening in that statement:
- to select/read we use the
SELECTclause - we use the
*to reference ALL columns - We use the
FROMclause to point our statement to theuserstable
It's great to get all the data, but sometimes you just want a few items.
Challenge 2
-
Return only the name and age of all users
-
Displaying the following results:
Shane|31 Zack|26 Jon Paul|30 Nick|28 Emily|33
Challenge 2 Answer:
sqlite> SELECT name, age FROM users;If you have hundreds (or thousands) of rows in your table, you may want to get more specific and provide some limitations. In that case, you can use the following:
sqlite> select * from users limit 2;1|Shane|31|Charlotte|2015-11-30 04:48:49.839777|2015-11-30 04:50:12.119858
2|Zack|26|Mooresville|2015-11-30 04:49:32.560862|2015-11-30 04:49:32.560862This returns the first 2 records for us. Now, I'm sure you're thinking 'what if I want something different?' Well just like offsetting in bootstrap, you can offset in your SQL statement.
try running:
sqlite> SELECT * FROM users LIMIT 3,2;This will produce:
4|Nick|28|Kansas City|2015-11-30 04:51:17.870682|2015-11-30 04:51:17.870682
5|Emily|33|Charlotte|2015-11-30 04:54:17.030918|2015-11-30 04:54:17.030918What if you'd like to be more specific? Like... looking for a specific object?
sqlite> SELECT * FROM users WHERE name = "Jon Paul";Using the WHERE clause will return:
3|Jon Paul|30|Los Angeles |2015-11-30 04:49:56.959506|2015-11-30 04:49:56.959506You can dial in your search even further by adding multiple items to the WHERE clause by using the AND keyword.
sqlite> SELECT * FROM users WHERE name = "Nick" AND age = 28;will return:
4|Nick|28|Kansas City|2015-11-30 04:51:17.870682|2015-11-30 04:51:17.870682note: OR is also an option...
Additionally, you can use comparison operators. Go ahead and try it.
SELECT * FROM users WHERE age > 30;returns:
1|Shane|31|Charlotte|2015-11-30 04:48:49.839777|2015-11-30 04:50:12.119858
5|Emily|33|Charlotte|2015-11-30 04:54:17.030918|2015-11-30 04:54:17.030918Sometimes, you want to find something in the database, but you're unsure of the exact phrase. Thankfully, SQL supplies us with the % (wildcard) operator.
sqlite> SELECT * FROM users WHERE name LIKE '%Z%';returns:
2|Zack|26|Mooresville|2015-11-30 04:49:32.560862|2015-11-30 04:49:32.560862We've covered the basic SELECT actions, so let's move into the next stage.
Challenge 3
UPDATENick's (or anyone's) city to Los Angeles 2. start bySELECTing Nick and finding the object ID 3.UPDATEthe object- Use
SELECTto insure that the object has been updated
hint: you'll need to use the UPDATE and SET commands. If you get stuck, use this as a reference.
Challenge 3 Answer
sqlite> SELECT * FROM users WHERE name = "Nick";
4|Nick|28|Kansas City|2015-11-30 04:51:17.870682|2015-11-30 04:51:17.870682
sqlite> UPDATE users SET city = "Los Angeles" WHERE id = 4;
sqlite> SELECT * FROM users WHERE name = "Nick";
4|Nick|28|Los Angeles|2015-11-30 04:51:17.870682|2015-11-30 04:51:17.870682Updating objects is pretty straight forward. Next we'll look at creating new objects.
We can use SQL to INSERT new objects into our database as well. Obviously, this represents the create operation in Rails.
This is done by using the following syntax:
sqlite> INSERT INTO users (name, age, city) VALUES ("Andrew", 27, "Charlotte");note: this will produce an error at the moment
Looking closely you'll see that we are using the following keywords:
INSERTINTOVALUES
While obviously, this makes sense in context. Pleaes take a moment to review each part of the statement closely.
Thankfully, rails prevents us from adding data directly to the DB unless it's time stamped.
Go ahead and add in created_at and updated_at values.
INSERT INTO users (name, age, city, created_at, updated_at) VALUES ("Andrew", 27, "Charlotte", 11/29/15, 11/29/15);Selecting Andrew should return the following:
SELECT * FROM users WHERE id = 6;
6|Andrew|27|Charlotte|0|0Last but not least, lets take a look at how to DELETE an object.
Challenge 4
DELETEthe user with an id of 3SELECTall the users, to ensure the object was successfully deleted.
Challenge 4 Answer:
sqlite> DELETE FROM users WHERE id = 3;
sqlite> SELECT * FROM users;
1|Shane|31|Charlotte|2015-11-30 04:48:49.839777|2015-11-30 04:50:12.119858
2|Zack|26|Mooresville|2015-11-30 04:49:32.560862|2015-11-30 04:49:32.560862
4|Nick|28|Los Angeles|2015-11-30 04:51:17.870682|2015-11-30 04:51:17.870682
5|Emily|33|Charlotte|2015-11-30 04:54:17.030918|2015-11-30 04:54:17.030918
6|Andrew|27|Charlotte|0|0You'll notice that the ID 3 is gone. Deleting an object at the database level will permanantly remove the ID as well.
And, thats it! You've succesfully learned SQL crud actions and how they relate to rails.
Now we can jump into Active Record, but first, you'll need to get out of the dbconsole.
simply run .exit to get out.
Alright! time for us to implement some things.
okay, let's start by creating a new app. By now you should know the drill, but just in case... let's do another challenge
Challenge 5
- Create a new Rails app named todo
- Scaffold a Tasks Resource
- When building the Resource make sure to include: 4. name (string) 5. priority (integer)
- STOP!
- Navigate to the migration file in your text editor
- STOP!
Challenge 5 Answer:
$ rails new todo$ rails generate scaffold tasks name:string priority:integerOnce the challenge is complete, you should have the following migration file:
class CreateTasks < ActiveRecord::Migration
def change
create_table :tasks do |t|
t.string :name
t.integer :priority
t.timestamps null: false
end
end
endLet's break this down, line-by-line
# CreateTasks is inheriting from ActiveRecord
class CreateTasks < ActiveRecord::Migration
# A change method is being created
def change
# create table is a method that exists within rails
# create a tasks table
create_table :tasks do |t|
# t represents an iteration
# create a string column with the title of name
t.string :name
#createa an integer column with the name of priority
t.integer :priority
# create timestamps and don't allow them to be null (a lifesaver)
t.timestamps null: false
end
end
endNow you'll want to run the migration and actually CREATE the table by running $ rake db:migrate
Which will result in the following terminal output:
== 20151103215638 CreateTasks: migrating ======================================
-- create_table(:tasks)
-> 0.0012s
== 20151103215638 CreateTasks: migrated (0.0013s) =============================Make sure to check the SCHEMA to see your new table
Rails console is your friend. It's like a mashup of irb and db_console, but better than both!
Essentially, rails console loads your entire application into a REPL so you have access to your entire application from the command line.
I know what you're thinking, and yes... you're right. It saves you tons of time.
So how do you get into rails console?
It's like almost every other rails command. Simply type $ rails console and you'll be dropped into a prompt that looks kinda like this:
irb(main):001:0>
First we'll need to CREATE a few tasks, similar to how we INSERTED data using SQL.
Challenge 6:
- Open the Rails Console...
- Instantiate a new Task object
- Assign a value to name and priority (using hash syntax)
- save it to the database
hint: You may find the Rails Guides helpful for solving this problem. - Take a moment and review the SQL output in the terminal. (note: it should look like the example below)
- Add 4 or 5 more items through the rails console.
(0.2ms) begin transaction
SQL (1.1ms) INSERT INTO "tasks" ("name", "priority", "created_at", "updated_at") VALUES (?, ?, ?, ?)
[["name", "Pick up coffee"], ["priority", 1], ["created_at", "2015-11-04 05:19:09.834387"], ["updated_at", "2015-11-04 05:19:09.834387"]]
(0.7ms) commit transaction
=> trueChallenge 6 Answer:
irb(main):001:0> x = Task.new(name: "Pick up coffee", priority: 1)
=> #<Task id: nil, name: "Pick up coffee", priority: 1, created_at: nil, updated_at: nil>
irb(main):002:0> x.savebtw - a shorthand way to add items is by calling Task.create (this rolls new and save into one command)
Let's talk about seeds.
If you navigate to db/seeds.rb in your text editor you'll see that you will add data directly to the database.
Try creating a method that generates a random string.
def random_string(x)
('a'..'z').to_a.shuffle[0..x-1].join
endAfter that create a loop that
- runs 10 times
- utilizes the random_string method and applies that as the value for name
- selects a random number from 1 - 3 and applies that as the value for priority
10.times do
Task.create(name: random_string(8), priority: (1..3).to_a.sample)
endHere's what your seed file should look like:
def random_string(x)
('a'..'z').to_a.shuffle[0..x-1].join
end
10.times do
Task.create(name: random_string(8), priority: (1..3).to_a.sample)
endNow run rake db:seed
This will add 10 new random items to the database.
Thankfully, there is an easier way to add 'test data' to your application. My favorite solution is an app by the name of ffaker.
Challenge 7
- User the the ffaker ruby gems info page to add ffaker to your project
- While we're at it... Add Table Print to your project
- Make sure to
bundle install!
Challenge 7 Answer:
ffaker
#in your gemfile
gem 'ffaker', '~> 2.1'Table Print
# in your gemfile
gem 'table_print'Install
$ bundle installNow you can generate dummy data. Navigate to your the rails console and run the following command:
irb(main):001:0> Task.new(name: FFaker::HipsterIpsum.words, priority: (1..3).to_a.sample)Now, see if you can create a loop. Or better yet, add this to your seed file.

