Making Migrations

What are migrations

Migration files are small Ruby scripts that make changes to your database.

Some examples of cases where a migration is used: • To create a new table in your database and define the columns it will contain as well as their data types • To drop a table in your database (completely erase it and all of its data) • To add a column to an existing table in your database • To rename a column or table in your database • To add an index to a database column

Changing a migration file will not make a change to your database until the file is run i.e. with rake db:migrate

Migrations aren’t used to change add or remove data in your database. They’re only used to define the schema or structure of your database. We use them to set up the tables and define the columns that exist.

A migration is basically a set of instructions written in Ruby that define what structure we want to have in our database. Each migration normally contains a version of the database structure. As the structure of our database might change over the course of the project, migrations are like a version history of our database and how its evolved (e.g. migration 1: create our first table, migration 2: add a new column to the table, migration 3: a new table is added, etc.)

Creating a migration

To create a migration, we use rake from the command line:

rake db:create_migration NAME=a_descriptive_name_for_my_migration

For example, if I wanted to create a task management tool, I’d need a table to store all of my tasks. To begin, I’d create a migration with a nice name that tells me what I’m about to put in this particular migration (basically to make it easier to find later.) To make a migration that’ll hold the instructions to create my new table I’d do something like this:

rake db:create_migration NAME=create_tasks_table

Notice some conventions: a migration name is all lowercase separated by underscores (“_“)

Also, you should give it a pretty descriptive name. Good practices says you should keep one or two discrete activities to a migration, even if that means creating lots of migrations e.g. create_tablename_table, add_column_name_to_tablename, remove_column_names_from_tablename, etc.

Once the migration has been generated, you can edit the file created. Go to the /db/migrate folder inside of your project directory (ActiveRecord will create this automagically for you). Migration files are named in the format:

db/migrations/<date><randomhash>_<migrationname>.rb

And inside your migration you’ll find some boilerplate code like this:

class CreateTasksTable < ActiveRecord::Migration
		def change
		end
		end	

Creating a table with migrations

Inside of this file, we put a series of activerecord instructions that allow us to define the changes we’d like to make to the database. These go inside the change method, like so

class CreateTasks < ActiveRecord::Migration[5.0]
  def change
    
    create_table :tasks do |t|
     t.string :name
   end    
  end
end

This tells activerecord to create a table named tasks in our database and add a column named ‘name’ with a string type. What other types exist, good question! Our columns could also be

	t.boolean :column_name 	#a boolean (true or false)
	t.integer :column_name 	#a whole number
	t.string :column_name 	#a text string up to 256 characters long
	t.text :column_name 	# a long text string
	t.datetime :column_name # a date time object 
	t.decimal  :column_name # a decimal number 
	t.float  :column_name 	# a floating point decimal number 

Creating a Table

Remember once again that just because we’ve defined a migration file, it doesn’t mean anything has been done to our database. In order for our changes to take effect, we need to run the database migration command:

rake db:migrate —— creates and adds the migration 

If your migration ran successfully, you should see something like this:

		== CreateTasksTable: migrating ===============================================
		-- create_table(:tasks)
		-> 0.0009s
		== CreateTasksTable: migrated (0.0010s) ======================================

At this point, pop open the database in something like Base. If you look at the table structure you’ll notice that it’s created an extra column named id for you. This is the primary key for the database table and it’s used to uniquely identify all of the records. ActiveRecord knows every table will need this so it sets it up for you by default.

One other great thing active record can do is track when new data is added and modified on our tables. These ‘timestamps’ are super useful, but not included by default. Let’s change the table creation to add them.

Uh Oh! We did something wrong

What if you realize you made a mistake? Well thankfully activerecord handles this. You can use a rollback to undo a migration.

If you type:

rake db:rollback

Your database will be stepped back one version (one migration). You can do this over and over - if you have multiple migrations to undo.

Open the migration and add the following

class CreateTasks < ActiveRecord::Migration[5.0]
  def change
    
    create_table :tasks do |t|
     t.string :name
     t.timestamps
   end    
  end
end

Apply the changes

rake db:migrate

And voila you’ll have two additional columns created_at and updated_at that will be automatically managed by activerecord. It’ll give you timestamps of any new changes to data.

Adding a new column

As our project grows, we’ll add new migrations creating other tables. What happens if we want to add a new column to our table? We don’t want to have to rollback everything just to do that because we’ll loose all the data we’ve added to our database. Instead we can create a migration just for this action.

Let’s say we want to add a boolean flag to keep tabs on if a task is completed. We’ll need to ads this column in. First we’ll create a new migration

rake db:create_migration NAME=add_is_complete_to_tasks

Notice the descriptive name again! Then we’ll open it up

class AddIsCompleteToTasks < ActiveRecord::Migration[5.0]
  def change
    
    
  end
end

To change a table and add a column to it, activerecord gives us a command called add_column. It takes the following parameters:

add_column table_name, column_name, type, options

So we can do the following:

    add_column :tasks, :is_complete, :boolean

Basically, we’ve said add a column on the table ‘tasks’ that’s called ‘is_complete’ and is of type ‘boolean’

We can also add an optional parameter called default that’ll specify the default information to add to any new column

    add_column :tasks, :is_complete, :boolean, default: false

Finally we apply this to the database by using a migration

rake db:migrate

Open up your database to take a look, it should be added now too!

Other Actions

In addition to create_table and add_column, there’s two other common actions you might use in your migrations these are:

    remove_column :table_name, :column_name
	drop_table :table_name

Hopefully these are self-explanatory!

Important notes

  • If you rollback a create_table migration for a table that has had data inserted into it since you initially ran the migration, you will lose all of your data. Instead, create an entirely new migration file to make the changes you’re looking to make in this case.

  • The dates (the numbers) in your migration have to be unique. These identify the version of the migration and tell Sinatra/ActiveRecord what migrations have been applied to the database. You can’t and shouldn’t just copy and paste these files. Always use the command line helper with rake to generate a migration

  • If a migration has already been run, it will not be run again unless it has been rolled back

  • There are conventions on how you name your tables

    • The names of your tables should be unique
    • The names of your tables should be lowercase, without any spaces (use underscores instead)
    • You should name your tables in the plural i.e. you want a table named tasks not task. Note This is really important for how ActiveRecord maps tables and records into ruby later.

Learn more

×

Subscribe

The latest tutorials sent straight to your inbox.