Rails Migration — Part 1

Juzer Shakir
6 min readJul 28, 2021

--

Generating migration files.

Prerequisite:

Definition

Migration is one of the features of Active Record which enables us to modify our table in database in a structured and organized manner.

As I covered in the Active Record article, migrations allow us to modify or create the transformations of a database in Ruby, which means we don't have to worry about implementing SQL for the most part, however, for certain database features, we would require to use SQL.

This topic is very vast to be covered in a single article. So I have divided it into 3 parts, in this article (Part 1) we will cover:

  • How to generate tables and declare their attributes with the custom data type.
  • Understanding migration files.
  • Methods like create_table and special helpers like timestamp and references.
  • unique and index keys.
  • Destroy migration.

Part 2: Executing Migration Files
Part 3: Update Existing Tables

Following topics to be covered in this article

Migration File

To generate a table named authors with name and age as its attribute in the database, we give a migration like this in CMD:

rails generate model Author name:string age:integer

This is called a model generator and contains instructions for creating a relevant table. We specify the name of a table we want to create and attributes of it along with its data type.

After running the following command, we get the following output:

invoke  active_record
create db/migrate/20210721030949_create_authors.rb
create app/models/author.rb
invoke test_unit
create test/models/author_test.rb
create test/fixtures/authors.yml

It has created 4 files for us:

  • Migration file 👉 migrate/20210721030949_create_authors.rb
  • Corresponding model file 👉 app/models/author.rb
  • The other 2 files are corresponding test files of authors table.

Migration File Name Format

The above command invoked Active Record to run the migration and created a migration file automatically in db/migrate directory in this format : YYYYMMDDHHMMSS_migration_name.rb.

The YYYYMMDDHHMMSS represents a UTC of when migration was generated followed by an underscore followed by migration name, create_authors.

The migration file name will always be in the format of snake_case whereas the table name will be in CamelCase, So for the table name Author, its corresponding file will be create_author, for Product, its corresponding file will be create_product and so on.

A look at the migration file:

The CreateAuthors class has been generated for us which inherits from the Migration class of the ActiveRecord module. This means we have all of the instance methods available to use from theMigrationclass.

create_table

In the scope of change method, the create_table method takes 2 arguments, first, the name of the table in the database, authors and second, takes a block of attributes and its data-type as key:value pairs. The t.timestamps method is implicitly added by Active Record which will generate 2 columns, created_at and updated_at along with the primary key, id.

references

A foreign key is an attribute in a table where for each of its intsances it links to a row of another table.

These are used in a table whose in a relationship with another table so that each instance of it can link to anyone instance of the other table with its primary key. We need to explicitly define it as it isn’t declared for us implicitly.

Let's see an example of references

We create another table books so that each book is linked to an author from the authors table.

rails g model Book title:string author:references

g is short for generate. With references given to the table of authors, this will create a column or an attribute :

  • with a name formatted as table_name’_id , in our case authors_id
  • which is ourforeign_key.

For this reason, references method is also called a special helper method because it provides a simple shortcut to multiple functionality.

Running above migration will generate the following migration file:

The references method supports the following options, index and foreign_key. So if you want to set index to true, you can declare it as:

t.references :authors, foreign_key: true, index: true

comment

We can also specify comments to the table and its attributes explicitly in the migration file which would help people understand the data model.

class CreateAuthors < ActiveRecord::Migration[5.2]
def change
create_table :authors, comment: 'Authors who have published best-selling books' do |t|
t.string :name, comment: 'Name of Author'
t.integer :age
t.timestamps
end
end
end

:comment, is declared as key:value pair, where value contains the description of it. These are stored in the database itself. We can view these comments when running the migration, or in db/schema.rb file, or with Database Administration Tools such as MySQL Workbench or PgAdmin III. :comment is currently only supported by MySQL and PostgreSQL.

An alternate way to create a table

rails g migration CreateBook title:string authors:references

Our migration name here is CreateBook. Whatever migration name we declare, that name will be set as our migration class name.

We use Create keyword followed by the singular table name. This will tell Active Record to invoke the create_table method and create the following attributes with the column-type specified.

The difference between this migration and the earlier model migration is that this migration only creates a migration file and not the corresponding model file.

Another difference is that this migration doesn’t implicitly create the timestamps method in the migration file. So running this migration will not create created_at and updated_at columns. To avoid this, we would need to explicitly mention the t.timestamps method under the scope of the create_table method.

The timestamps method is also another special helper method.

However, the primary key will be implicitly created with the default name as id.

Setting unique to an attribute

rails g model Book title:string:uniq

generates…

Giving unique to the title attribute invokes add_index method, so not only our attributes’ values will be unique but its index is also set to true.

What is Index?

If you have a large database with millions of records and high traffic, the responses become slow, so extracting data becomes more time-consuming as records increase. If we’re extracting the same attribute, again and again, for example, email or username or a foreign key, using index to it would make operations faster & efficient as queries would be easily searchable because indexcreates a copy of that attribute and sorts them in alphabetical order.

For more info on this, refer to this post.

Unique

It does not allow duplicate values in the same column/attribute in a table.

Supported column-types

Active Record supports the following database column types:

  • binary
  • boolean
  • date
  • datetime
  • decimal
  • float
  • integer
  • primary_key
  • string
  • text
  • time
  • timestamp

You may use a type that is not in this list as long as it is supported by your database, for example, using polygon in MySQL. But this won’t be database agnostic and should usually be avoided.

This article gives a detailed explanation of each column type and what column types are available to use for different databases.

Delete Migration Files

Now let’s see how to destroy migration files, assuming we have a model class named Book:

rails destroy model Book

Output:

Running via Spring preloader in process 33047
invoke active_record
remove db/migrate/20210721053723_create_books.rb
remove app/models/book.rb
invoke test_unit
remove test/models/book_test.rb
remove test/fixtures/books.yml

All 4 files that were created when running generate, will be deleted with destroy!

Warning:
If you have already run the migrations on it, you need to manually rollback those migrations that created or updated this table in anyway, so that it updates the schema accordingly and we don’t run into errors when running db:migrate task, as destroy command just deletes the files that generated when migration was ran and not the table in database, there's no coming back from it!

After creating migration files, in Part 2 we will look into how to generate a corresponding table of it in the database.

Part 2: Executing Migration Files

--

--