ActiveRecord Migration Rollback: learning the pitfalls of the `change` method the hard way
Raquel Román-Rodriguez
Posted on August 9, 2021
I recently finished my project for the third phase of FlatIron's Software Engineering Bootcamp. I built a raised-bed garden planning app using React for the front-end, and Ruby with ActiveRecord and Rack for my back-end. It was my first real back-end experience. You can check out the deployed app here.
I got to learn a lot from my own mistakes on this project, and one mistake in-particular caused such grief that I'm hoping this blogpost can serve as a word of caution to others learning about ActiveRecord Migrations.
The gist of this is that not all ActiveRecord Migrations support the change
method equally (or at all). This is well-documented in the Active Record Migration docs, a section that apparently eluded my eyes before disaster struck.
The Mistake
Calling this "a mistake" is a bit misleading. It really was a chain of mistakes caused by my own lack of understanding of ActiveRecord migrations and rake commands.
This chain of mistakes started with the 37 records of my Plant model and three times this of my Note model that I needed to import into my database.
I thought I'd be cute, give myself some extra 'controlled forms in React' and 'writing routes in Rack' practice by making a form to POST
this data to my back-end. A time-wasting labor that I destroyed in a single command later.
My next misstep was when I realized I had an incorrect association in my models. To fix it, I wrote a migration to remove the foreign id:
class RemoveGardenIdColumnFromPlants < ActiveRecord::Migration[6.1]
def change
remove_column :plants, :garden_id
end
end
After migrating this, I created a new model and yet another migration. I went to check my schema and realized the association from my remove_column
migration was still in the Plants table, because this was not the correct way to remove an association id.
"No problem," I thought, "I'll roll it back, try again."
"HA! Not so fast!" said ActiveRecord:
===========================================================
rake aborted!
StandardError: An error has occurred, this and all later migrations canceled:
remove_column is only reversible if given a type.
Here's where things go very south for me.
It's important to note that up until this point, it was absolutely possible for me to reverse this migration and not lose my data in the process.
Taking a break before the panic starts might be the most important lesson here.
I began googling, trying things with increasing recklessness as my frustration grew. It was here that I misread a Stack Overflow question thinking it was asking how to solve the problem I was facing, and made the fatal mistake of running rake db:drop
. While this did allow me to remove the migration mistakes, I also lost all of my data I had spent time submitting through that form I made.
🙃 Awesome.
Avoiding the Mistake
First and foremost: When something goes wrong, especially when working with data, STOP. Take a break. Go for a walk. Mistakes with database commands can have dire consequences that cost you precious time. And don't you dare run that #@$!-ing command until you know what it does.
Once you've stepped away from the problem, come back to it, and read advice, carefully.
In writing this post, I re-read the first answer I had come across that had told me to change the migration file to use an up
and down
method in the remove_column
migration. I did try this, but received the same error.
The next line of that answer explained how to rollback to a specified version using rake db:down VERSION=<your-version-number-here>
. I tried this, trying to rollback to the first version of my migration, which also did not work, because what this line was actually telling me was that, since I had run another migration after the remove_column
, I needed to rollback to the remove_column
migration, and then try to rollback with up\down
implemented.
The Solution
If you've created another migration after the irreversible one, rollback to the version of the irreversible migration using
rake db:down VERSION=<your-version-number-here>
Change the irreversible migration file to use
up
anddown
instead of change, like so:
class RemoveGardenIdColumnFromPlants < ActiveRecord::Migration[6.1]
def up
remove_column :plants, :garden_id
end
def down
add_column :plants, :garden_id, :integer
end
end
or using change
:
class RemoveGardenIdColumnFromPlants < ActiveRecord::Migration[6.1]
def change
remove_column :plants, :garden_id, :integer
end
end
ActiveRecord is an awesome tool, but migrations can be a steep learning curve. Take your time, take a breath, and don't cry over dropped tables.
Posted on August 9, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
August 9, 2021