A review of simple web form, and how Ruby-on-Rails handle them. From scratch.

· Ruby  · 5 min read

Rails add column

A review of simple web form, and how Ruby-on-Rails handle them. From scratch.

Introduction

Let’s say you have a table named “books”, and an associated model named “Book”.

You want to add the column “author”.

Tools used in this tutorial : Rails 8.0.0, Ruby 3.3.0

Note that this article should work pretty well with any version of Rails or Ruby.

Short answer

For those who already know Rails, here is the short answer :

inside db/migrate/20240131201926_add_author_to_books.rb

class AddAuthorToBooks < ActiveRecord::Migration[6.1]
  def change
    add_column :books, :author, :string
  end
end

And then run bin/rails db:migrate at the root of your project.

Full tutorial

Install new minimal app

For this tutorial we don’t need a full Rails app with bells and whistles, the bare minimum will suffice. From Rails 6.1, the —minimal flag is available when you create a new Rails app.

$> rails _8.0.0_ new myapp --minimal
$> cd myapp

By default, Rails will use an in-memory database named SQLite3.

Side note If you want to start with PostgreSQL, you can enter the following command :

$> rails _8.0.0_ new myapp --minimal --database=postgresql
$> cd myapp

Create the database

$/myapp> bin/rails db:create
Created database 'myapp_development'
Created database 'myapp_test'

Create a first model

$/myapp> bin/rails generate model Book title:string body:text

Note that “bin/rails” will use the installed rails binary for the current application, instead of the globally available one.

This will create many files :

      invoke  active_record
      create    db/migrate/20240331122059_create_books.rb
      create    app/models/book.rb
      invoke    test_unit
      create      test/models/book_test.rb
      create      test/fixtures/books.yml

You may not need everything. The interesting thing is the migration file created under db/migrate/20210131201925_create_books.rb (The timestamp represent the current time when file was created, of course you will have another than this example)

class CreateBooks < ActiveRecord::Migration[6.1]
  def change
    create_table :books do |t|
      t.string :title
      t.text :body

      t.timestamps
    end
  end
end

Then run the migration

$/myapp> bin/rails db:migrate
== 20210131201925 CreateBooks: migrating ======================================
-- create_table(:books)
   -> 0.0019s
== 20210131201925 CreateBooks: migrated (0.0020s) =============================

Add a migration file

Create a file named db/migrate/20240131201926_add_column_to_books.rb

Check the timestamp, it has to be a greater number than the one of 20240131201925_create_books.rb previously created. Or the migrations won’t be able to be run in the correct order.

class AddColumnToBooks < ActiveRecord::Migration[6.1]
  def change
    add_column :books, :author, :string
  end
end

Migration file analyzed

We’ve already seen the timestamp. Now the name of the migration : it actually could be anything. But try to make the intent clear, for yourself or other developers : here I put add_column_to_books, a better name could have been add_author_to_books. Anyway, make sure that the filename (apart from the timestamp) is the same as the class name.

Now we inherit from ActiveRecord::Migration, for pretty obvious reasons : we want to migrate the data, which is a pretty common behavior, common enough to be hidden inside a standard Rails object, ActiveRecord::Migration.

The number indicates the 2 first numbers of the Rails version. Here I use Rails 8.0.0, thus the two numbers are [6.1]. It’s because features could be available in ActiveRecord::Migration[6.1] that were not available in ActiveRecord::Migration[6.0].

Then the “change” method. You cannot (ahem) change the name, because this method is automagically called when you run bin/rails db:migrate.

Finally, add_column is pretty clear : first arg is the name of the table, then the name of the column, and then the kind of column.

Migrate the data

bin/rails db:migrate Will actually add the column to your current database.

$/myapp> bin/rails db:migrate
== 20240131201926 AddColumnToBooks: migrating =================================
-- add_column(:books, :author, :string)
   -> 0.0021s
== 20240131201926 AddColumnToBooks: migrated (0.0022s) ========================

Check the schema.rb file

You can see that the column has been added inside your schema.rb file here :

ActiveRecord::Schema.define(version: 20240131201926) do

  create_table "books", force: :cascade do |t|
    t.string "title"
    t.text "body"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.string "author"
  end

end

The added column appear here : t.string "author"

The migration “appears” in another place : the timestamp. Look at ActiveRecord::Schema.define(version: 20210131201926) . The timestamp is the one of your migration files.

Bonus : Inspect created tables - if PostgreSQL is used

If you have used the default SQLite3, please read the next paragraph. If you have chosen PostgreSQL, this is the right paragraph :)

With Rails, you can inspect the actual database by entering the command :

$/myapp> bin/rails db
psql (13.1)
Type "help" for help.
myapp_development=#

Great ! You just entered the PostgreSQL command-line interface (CLI).

myapp_development=# \dt
List of relations
Schema | Name | Type  | Owner
--------+----------------------+-------+-------
public | ar_internal_metadata | table | shino
public | books                | table | shino
public | schema_migrations    | table | shino
(3 rows)

Great ! As you may notice, Rails creates 2 tables by default : ar_internal_metadata, and schema_migrations.

We are only concerned by the “books” table.

Let’s display it :

Inside Books Table
Inside Books Table

Bonus : Inspect created tables - if SQLite3 is used

This is the same paragraph as above, but the command will apply to a SQLite3 database only (which is the default one with Rails)

$/myapp> bin/rails db
SQLite version 3.28.0 2019-04-15 14:49:49
Enter ".help" for usage hints.
sqlite>

Then, to display tables

sqlite> .headers on
sqlite> .mode columns
sqlite> .table
ar_internal_metadata  books schema_migrations

Then, to display the internals of the “Books” table :

sqlite> PRAGMA table_info('books');
cid         name        type        notnull     dflt_value  pk
----------  ----------  ----------  ----------  ----------  ----------
0           id          integer     1                       1
1           title       varchar     0                       0
2           body        text        0                       0
3           created_at  datetime(6  1                       0
4           updated_at  datetime(6  1                       0
5           author      varchar     0                       0

The magic way : generate migration

Another possibility is to use the scaffolding, but in this case you have to pay extra attention to the naming of your file :

$/myapp> bin/rails generate migration AddAuthorToBooks author:string

or

$/myapp> bin/rails generate migration add_author_to_books author:string

And the migration file will be filled automagically with all correct values, timestamp included.

In everyday work, I don’t use this magic. I simply copy/paste other migrations, paying attention to the name of the file, and that’s it.

Options for add_column

You can see a full list of all available options in this article : https://api.rubyonrails.org/classes/ActiveRecord/Migration.html

Conclusion

Adding a column with Rails is easy, I would say the important thing is to know what’s going on, and why.

Share:
Back to Blog

Related Posts

View All Posts »
How to create tons of Rails apps

How to create tons of Rails apps

Yes, I already now about the rails new command. But what if you want better defaults, like an existing controller, an existing view, or another way to to launch the Procfile?

Rails new options

Rails new options

Let's see the official way to create Rails application, as well as the minimalistic option.

Rails active support

Rails active support

A review of the benefits of Rails ActiveSupport, through an interview of a Rails teacher.