Rails Migration — Part 3
Updating existing tables with different migration methods.
Prerequisite:
This article is a continuation of previous articles:
Table of Contents
↦ Migration Methods
↪ change
↪ ↪ Attribute Modifiers
↪ change_table
↪ up & down
↪ reversible
Standalone Migration
In my previous article, we had 2 migration files that created 2 tablesauthor
and book
with some attributes, after running the migration. Now let’s modify our table by generating a new migration file:
rails g migration AddColumnsToAuthors
which will create the following file:
db/migrate/20210725125500_add_columns_to_authors.rb
this will create an empty migration file also called a Standalone Migration file:
But if we gave attribute name and its type to the above migration task, like:
rails g migration AddColumnsToAuthors nationality:string
then it generates the migration file like this:
We see that it implicitly added the add_column
method with an attribute name nationality
as string
type to authors
table.
A migration name of this form AddXxxToTable
followed by attribute name and type, attribute_name:attribute_type
, will invoke add_column
method to add an attribute to the appropriate table as mentioned in migration name, Table
.
Remember migration name should be in CamelCase and column names in snake_case.
Similarly, if we had the migration name of the form RemoveXxxFromTable
followed by attribute name and type, it will invoke remove_column
method like:
rails g migration RemoveColumnsFromAuthors nationality:string
which generates a migration file like this:
Now, in total, we have 4 migration files and if we run db:migrate
, this would not modify our table in any way as 3rd migration creates an attribute and the 4th migration deletes it, it doesn’t update our schema file in any meaningful way so let's delete the 3rd and 4th migration files. We can delete manually both files with GUI or from CMD we can run the following:
$ rails d migration RemoveColumnsFromAuthorsremove db/migrate/20210725132852_remove_something_from_authors.rb$ rails d migration AddColumnsToAuthorsremove db/migrate/20210725131028_add_something_to_authors.rb
We give command d
, short of destroy
, followed by migration file name.. Works the opposite of command g
/ generate
migration task.
Note : Do not delete migrated files. First rollback those and then delete it.
However, we are not limited to just add_column
and remove_column
methods.
Migration Methods
change method
Official ruby documentation lists every method available to use under the change
method in the migration file. Here’s a snap of it:
However, not all methods are reversible. Here’s a list of reversible methods. In addition to those methods, there are a couple more: change_column_comment
& change_table_comment
, and for both of these to be reversible we need to pass hash containing :from
and :to
. For methods that aren’t reversible, use those methods under up
& down
method or reversible
method instead of change
method. (elaborated in a later section)
So let's apply some of the migration methods to modify our table in the database.
To do:
In authors
table:
- Change an attribute name
name
tofull_name
. - Add new attributes:
original_language
,nationality
,genre
asstring
type. - Add new attributes:
fiction
asboolean
type and setdefault
as true. - Add new attributes:
max_est_sales
(maximum estimated sales) as aninteger
type and set itslimit
as8
. - Add new attribute:
major_works
astext
type.
Attribute Modifiers:
These are the modifiers that are available to us when we want to create or change an attribute of a table:
:limit
👉 Sets maximum character limit for a :string
attribute-type whereas for :text
, :binary
, and :integer
attribute-types, it sets the maximum number of bytes.
:default
👉 Sets attributes’ default value. Use nil
for NULL
. (cannot be provided through the command line)
:null
👉 Allows or disallows NULL
values in the column. Set false
to disallow null value. By default, all attributes null values are set to true
. (cannot be provided through the command line).
:precision
👉 Allows the total number of digits which includes before and after a decimal point. For example, if precision
is set to 5
for an attribute, it can have values in the following form: 99999, 999.99, 9.9999, etc. We can specify precision
for these attribute types: :decimal
, :numeric
, :datetime
and :time
.
:scale
👉 Total number of digits that are allowed after a decimal point. We can specify precision
for these attribute types: :decimal
and :numeric
.
:comment
👉 Specifies the comment for the column. Can be specified for the table too.
:if_not_exists
👉 If the column already exists, do not try to re-add it. This will avoid duplicate column errors.
All of these modifiers are great tools for us to store the desired value in tables. And we will use a few of these modifiers,default
and limit
, to complete the ‘To Do’ task.
Note: there are many other modifiers available for other methods, more on this here.
To complete our ‘To Do’ task, let's give a standalone migration as:
rails g migration AddDetailsToAuthors
Adding fiction
and max_est_sales
attributes with their modifiers to migration file:
rails db:migrate
Our schema file:
change_table method
Similar to the create_table
method we looked at in part 1, change_table
is used to update the existing table in our database and it has more methods available. And it too is defined in the scope of the change
method in the migration file.
List of methods available to use:
Our ‘To Do’ task is not yet complete as we do have to add some more attributes to our table. Let's create another standalone migration and use the change_table
method.
Our migration file:
Instead of using change_table
method, we could also use the add_column
method as an alternative to achieve the following task but then we would need to explicitly specify the table name :authors
each time we gave add_column
method. So, the change_table
provides a little shortcut by yielding table :authors
inside the block of change_table
.
This will update our schema file accordingly.
All of the methods of the change_table
are reversible except for change
, change_default
, remove
. So if we gave any of these 3 methods inside the change_table
method and executed db:rollback
(assuming they’re migrated), it would raise an error.
Let's try it out by removing the fiction
attribute with the remove
method.
After migrating this file, if we reverse this migration file with rails db:rollback
, it will give us the following error:
Caused by:
ActiveRecord::IrreversibleMigration:This migration uses remove_columns, which is not automatically reversible.
To make the migration reversible you can either:
1. Define #up and #down methods in place of the #change method.
2. Use the #reversible method to define reversible behavior.
It invoked the IrreversibleMigration
class which tells us how to overcome this error. There are 2 ways, instead of using change
method, use up
& down
or reversible
method.
up & down method
Instead of change
method, we gave 2 methods, up
& down
. When running rails db:migrate
, it triggers to execute the up
or change
method for each migration file and in this example it executes the up
method which deletes the specified attribute. When running rails db:rollback
it executes thedown
method which creates the attribute that was deleted.
So in a nutshell, The
up
method should describe the transformation you'd like to make to your schema, and thedown
method should revert the transformations done by theup
method. In other words, the database schema should be unchanged if you executedup
followed by adown
. For example, if you create a table in theup
method, you should drop it in thedown
method.
If for some reason migration is irreversible, then we should raise ActiveRecord::IrreversibleMigration
in down
method, so if someone tries to revert our migration, an error message will be displayed saying that it can't be done.
reversible method
Inside the change
method, we give reversible
method and giving up
and down
methods inside its block.
The difference between the up
& down
and thereversible
method is that in reversible
, up
& down
methods are inside the block of reversible
which itself is inside the block of change
method. It's a syntactical difference rather than logical. You can use any of these 2 methods which suit you.
These methods should be used when Active Record can’t reverse a small part of a migration, for instance when using raw SQL statements.
Well, that’s all the basics we need to know about AR Migrations. It was long 3 articles that covered different aspects of AR Migration, and if you have read all of them, I am humbled and I thank you sincerely.