Migration Basics

Migrations are maintained in files stored in the db/migrate folder. Each file contains one more-or-less discrete set of changes to the underlying database. Unlike most of the code you write, migrations are not automatically run when you start up Rails, instead waiting for an explicit command from the rake tool.

Migration Files

Prior to Rails 2.1, migration files had relatively comprehensible names, such as 001_create_people.rb. Rails 2.1 brought a new naming convention, in which the first part of the name changed from being a sequential number to being a much longer timestamp, like 20080701211008_create_students.rb. The new wider names are incredibly annoying if you’re just one developer creating applications on a laptop with a narrow screen, but help avoid name collisions if you’re a developer working on a team where multiple people can check in their own migrations. (Perhaps the team developers have larger monitors as well?)

While you can create migration files by hand, if you’re going to work with migrations in the new world of timestamps, you should probably stick to using script/generate, which will handle all of that for you. Many script/generate calls will create migrations as part of their work toward creating a model and supporting infrastructure, but if you just want to create a blank migration, enter the command script/generate migration NameOfMigration, where NameOfMigration is a reasonably human-comprehensible description of what the migration is going to do. (For the name, you can use CamelCase or underscores_between_words.) Your result, depending on the name you give it, will look something like Example 10-1.

Example 10-1. An empty migration file, fresh from script/generate

class EmptyMigration < ActiveRecord::Migration
   def self.up
  end

  def self.down
  end
end

All migrations are descended from ActiveRecord::Migration. The self.up and self.down methods are the heart of the migration. In theory, at least, they should be strictly symmetrical. Everything created in self.up should vanish when self.down is called, leaving the database structure in the same state it had before the migration was run. If you let these two methods get out of sync, you’ll have a very hard time recovering.

Warning

While you’ll obviously be paying attention when writing your applications, and the generators shouldn’t create problems, there’s one situation that might still bite you: an unsaved file you’re editing. If you think you’ve made changes and run the migration forward, but didn’t save the file, and then save the file and roll the migration back....

Unfortunately, fixing it really depends on what exactly you did. Just be careful to make sure that you’ve saved all of your files when editing migrations before running them.

Most of the rest of the chapter will examine what you can put inside of those two methods.

Running Migrations Forward and Backward

You apply migrations to the database using the Rake tool. You can run rake --tasks to see the ever-growing list of tasks it supports, and most of the database-related tasks are prefixed with db:. (In Heroku, you get to rake through the gear menu.) While you’re learning Rails, there are only three tasks that you really need to know, and a fourth you should know about:

db:migrate

You’ll run rake db:migrate frequently to update your database to support the latest tables and columns you’ve added to your application. If you run your application and get lots of strange missing or nil object errors, odds are good that you forgot to run rake db:migrate. It also updates the db/schema.rb file, which is a one-stop description of your database.

db:rollback

If you made changes but they didn’t quite work out, rake db:rollback will let you remove the last migration applied. If you want to remove multiple migrations, you can specify rake db:rollback STEP=n, where n is the number of migrations you want to go back. Be careful—when Rails deletes a column or table, it discards the data. It also updates the db/schema.rb file, which is a one-stop description of your database.

db:drop

If things have gone really wrong with your migrations, rake db:drop offers you a “throw it all away and start over” option, obliterating the database you’ve built—and all its data.

db:reset

Using rake db:reset is a little different from using rake db:drop—it obliterates the database and then builds a new one using the db/schema.rb file, reflecting the last structure you’d created.

db:create

The rake db:create command tells the database to create a new database for your application, without requiring you to learn the internal details of whatever database system you’re using. (You must, of course, have the right permissions to create that database.)

Most of the time, rake db:migrate will be your primary interaction with rake. When you run it, it will show information on each migration it runs, as shown in Example 10-2, using the migrations from the previous chapter. (The start of each migration is bolded to make it easier to review the output.)

Example 10-2. Output from Rake, for a set of four migrations

S:students001c simonstl$ rake db:rollback
(in /Users/simonstl/Documents/RailsOutIn/current/code/ch09/students001c)
S:students001c simonstl$ rake db:migrate
(in /Users/simonstl/Documents/RailsOutIn/current/code/ch09/students001c)
== 20080701211008 CreateStudents: migrating ============================
-- create_table(:students)
   -> 0.0046s
== 20080701211008 CreateStudents: migrated (0.0048s) ===================

== 20080701211027 CreateAwards: migrating ==============================
-- create_table(:awards)
   -> 0.0054s
== 20080701211027 CreateAwards: migrated (0.0056s) =====================

== 20080705141325 CreateCourses: migrating =============================
-- create_table(:courses)
   -> 0.0031s
== 20080705141325 CreateCourses: migrated (0.0034s) ====================

== 20080705141333 CreateCoursesStudents: migrating =====================
-- create_table(:courses_students, {:id=>false})
   -> 0.0026s
-- add_index(:courses_students, [:course_id, :student_id], {:unique=>true})
   -> 0.0039s
== 20080705141333 CreateCoursesStudents: migrated (0.0071s) ============

The timing information may be more than you need to know, but you can see what got called in what migration. If something goes wrong, it will definitely let you know.

Note

Rails will happily let you perform operations on multiple tables from within a single migration. Eventually, that may be an attractive option, but when you’re first starting out, it’s usually easier to figure out what’s going on, especially what’s going wrong, when each migration operates only on a single table.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset