High Leverage Rails
Introduction
Introduction to this course
Why use Ruby on Rails
Why use SQLite
Ruby on Rails + SQLite
Powering Your App with SQLite
Creating tables
Timestamps
Column types
Typeof
Ruby types
Creating table introduction
Creating table advanced
Inserting data
Updating data
Upserting data
Reading data
Virtual columns
Enums
Introduction to JSON
Indexing JSON
JSON table functions
Building a Modern Rails Application
Creating a new Rails application
Installing Solid Queue
Installing Solid Cache
Installing Solid Cable
Dockerfile
Application overview
Authentication
Base styles
Registration
Scaffolding posts
Polishing posts
Scaffolding comments
Polishing comments
Polishing models
Polishing controllers
Creating new post
Updating post
Reviewing MVP
Tagging posts
Custom tags
Friendly URLs
Full text search
Deploying & Operating Your App
Backups
Check Litestream locally
Verifying backups
Deployment options
Deploying with Hatchbox
Deployment constraints
Vertical scaling
Database access
Migrations
Locked video

Please purchase the course to watch this video.

Video thumbnail
Powering Your App with SQLite
Creating table introduction

Full Course

$
129
$179
USD, one-time fee
This course came at the perfect time. I’ve recently gotten back into Rails after an 18-year hiatus, and this was a perfect refresher and shows just how much you can accomplish with Rails right out of the box.
Garrett Winder
Garrett Winder

Rails hosting made simple

Deploy apps to servers that you own and control.

Move fast and fix things

Application monitoring that helps developers get it done.

Summary

Learn how to insert records into a database using Active Record in Rails with methods like create!, create, and save. Discover how prepared SQL queries with bind parameters enhance performance and security while preventing SQL injection. Plus, explore error handling strategies and the role of transactions in ensuring data integrity.

Video Transcript

Now that we know how to define schemas for our tables, let’s focus on inserting records into these tables using the Active Record interface.

Jumping back into an IRB session playground, I now have a more realistic posts table with a title and body column. Let’s set this up, create our posts table, and look at how we can create a new post.

Using create! to Insert Records

The most common method to create a new record is the create method. We add this 'bang', the exclamation point and we call it on our Post model and pass in values like this:

Post.create!(title: "title one", body: "some text")

When we run this, the logs reveal three SQL queries being executed:

  1. Beginning a new transaction
  2. Inserting the record into the posts table
  3. Committing the transaction

Why Active Record Uses Transactions for Inserts

Active Record wraps inserts in a transaction to ensure data integrity. This guarantees that records are durably and safely written to the database, preventing incomplete writes or corruption.

Looking at the INSERT INTO statement, we can see:

  • Active Record maps our model (Post) to the correct table (posts).
  • It prepares a generic SQL query with bind parameters (?).
  • Values for title, body, created_at, and updated_at are inserted.

Performance Benefits of Bind Parameters

Using bind parameters in SQL queries provides a performance boost by:

  • Reusing a single SQL query instead of preparing a new one for each insert.
  • Preventing SQL injection by escaping values properly before insertion.
  • Ensuring safer and more efficient database operations.

Once the data is inserted, SQLite returns the id of the newly created record, which Active Record assigns to the Post instance.

Create method output

We see that this method, the create method, returns an instance of our post class.

post1

post1 is our new post record. The final detail worth noting is that at the end of this equery have have this RETURNING "id" clause. SQLite is going to return a subset of columns from this insert into Active Record. All I really need is the ID. What was the auto incremented primary key that the engine used to fill in this value? We want our actual post instance to have that value when we look at it.

The fact that it only returns ID is in fact the source of the problem that we saw last time, where a time column, when we were originally looking with our post instance had a specified year, month, and day though in the database it had been resolved into just January 1, 2000.

Unfortunately, as of right now, there is no way to force active record to return a different set of columns when you're using the create method. Hopefully this is something that will be addressed in the future.

Handling Errors: create! vs. create

Active Record provides two variations of the create method:

  1. create! (With Exclamation Point) – Raises an Exception on Failure
post = Post.create!(title: nil) # Throws an error if validation fails
  • If something goes wrong, it raises an exception immediately.
  • Best for ensuring that data is inserted correctly.
  • Useful in scenarios where failure should not go unnoticed.
  1. create (Without Exclamation Point) – Returns false on Failure
post = Post.create(title: nil) # Returns `false` instead of raising an error
  • Returns false instead of an exception.
  • Useful for cases where you want to check success manually.
  • Can be used inside if-else statements to handle failures gracefully.

Only use the bare version if you're 100% certain of what you need and you know you only need the boolean response.

Using new and save for More Control

Another way to create records is by initializing a Post object first and saving it later:

post3 = Post.new(title: "title two", body: "some more text")
post3 # Returns an in-memory Ruby object without an id or timestamps.

No SQL queries have been run yet. We can make changes to this object:

post3.title = "Updated"

Again no SQL queries have been run. At any point we can call save on this object and this will execute a SQL query.

post3.save!
  • Initially, post is just a Ruby object (not saved to the database yet).
  • Modifications can be made before committing to the database.
  • save triggers an SQL INSERT when called.

This method is useful when you need to modify data before saving it or when creating records in multiple steps.

Why This Matters for Performance

By using prepared statements and reusing the same SQL query shape, SQLite does not have to recompile the SQL query each time, leading to faster repeated insertions. This approach:

  • Speeds up record creation in high-traffic applications.
  • Reduces database load, making Rails apps more efficient.

Summary

With Active Record there are 3 ways you can create records.

  1. You can create! them ensuring that any problems raise exceptions.
  2. You can give yourself the ability to manage error handling just using create without an exclamation point.
  3. If you need to have a bit of a gap between the place where you initialize your Ruby object and the place where you actually run the SQL query, you can use the new method to instantiate a new ruby object. Then you can call save at any point, any place in your program and that will actually execute this pattern of the transaction, insert into, and the commit.

In the next video, we’ll explore additional ways to insert records and discuss when to use different Active Record methods for creating records in real-world Rails applications.