ActiveRecord::Migration
No documentation available for this class.
Files
- activerecord/lib/active_record/migration/command_recorder.rb
- activerecord/lib/active_record/migration/compatibility.rb
- activerecord/lib/active_record/migration/default_strategy.rb
- activerecord/lib/active_record/migration/execution_strategy.rb
- activerecord/lib/active_record/migration/join_table.rb
8Notes
Migration helpers
You can add your own migration helpers as references:
==== Code example
class ActiveRecord::ConnectionsAdapters::TableDefinition
def counter_caches(*args)
args.each { |col| column("#{col}_count", :integer, :default => 0) }
end
end
class CreateUsers < ActiveRecord::Migration
def self.up
create_table :users do |t|
t.string :first_name, :last_name, :email
t.counter_caches :photos, :messages
t.timestamps
end
end
def self.down
drop_table :users
end
end
Loading fixtures in migrations
This helper is wrapper around Fixtures#create_fixtures and just load fixtures from specified directory (db/migrate/data by default):
class ActiveRecord::Migration
def self.load_data(filename, dir = 'db/migrate/data')
Fixtures.create_fixtures(File.join(RAILS_ROOT, dir), filename)
end
end
It is usefull for tables with data like country list:
class CreateCountries < ActiveRecord::Migration
def self.up
create_table :countries do |t|
t.string :name, :code, :null => false
t.timestamps
end
load_data :countries
end
def self.down
drop_table :countries
end
end
Calling migrations within migrations
It's very occasionally a wise strategy to call migrations from within other migrations. This is typically done when you are adding a migration that deletes a now-obsolete table.
Let's say one night when you were drunk or otherwise not thinking straight you did something like this:
class CreateExGirlfriendTexts < ActiveRecord::Migration def self(dot)up create_table :ex_girlfriend_texts { |t| ... } end
def self(dot)down drop_table :ex_girlfriend_texts end end
Oops! You could add this for your "undo" migration the next morning:
class FixDrunkMistake < ActiveRecord::Migration def self(dot)up CreateExGirlfriendTexts.down end
def self(dot)down CreateExGirlfriendTexts.up end end
Now, in the event you decide you really did like that table, you can always get it back easily. Keep in mind this will be made more complicated if your table is modified over multiple transactions.
HABTM relation
When you want to create a +has_and_belong_to_many+ relation (og just a +has_many+ +:through+) use this setup.
===== Example class CreateCourses < ActiveRecord::Migration def self.up create_table :seasons do |t| t.integer :year t.string :period end
create_table :courses do |t|
t.string :courseCode
end
create_table :courses_seasons, :id => false do |t|
t.references :course, :null => false
t.references :season, :null => false
end
add_index :courses_seasons, [:course_id, :season_id], :unique => true
end
def self.down
drop_table :seasons
drop_table :courses
drop_table :courses_seasons
end
end
Calling migrations within migrations observation
Following the advice from RISCfuture I could not call a migration from within another migration. I got the following errror message:
NameError Exception: uninitialized constant FixDrunkMistake::CreateExGirlfriendTexts.down
Only after I did a
require 'create_ex_girl_friend_texts' # the migration file
before the migration call did everything work as expected.
Using models in your migration
Here is some advice how to call your models in a migration without shooting yourself in the foot:
http://gem-session.com/2010/03/how-to-use-models-in-your-migrations-without-killing-kittens
Basically you can inline models into your migrations to decouple them from changes in your model:
class AddCurrentToVendor < ActiveRecord::Migration
class Vendor < ActiveRecord::Base
end
class Article < ActiveRecord::Base
has_many :vendors, :class_name => 'AddCurrentToVendor::Vendor', :order => 'created_at'
end
def self.up
add_column :vendors, :current, :boolean
Article.all.each do |article|
article.vendors.first.andand.update_attribute(:current, true)
end
end
def self.down
remove_column :vendors, :current
end
end
Positioning the column. MySQL only
Add support for MySQL column positioning via #add_column and #change_column
add_column and change_column in the MySQL adapter now accept some additional options:
:first => true # Put the column in front of all the columns
:after => column_name # Put the column after 'column_name'
class AddLastNameToUsers < ActiveRecord::Migration
def self.up
add_column :users, :last_name, :after => :first_name
end
def self.down
remove_column :users, :last_name
end
end
or
class AddIdToUsers < ActiveRecord::Migration
def self.up
add_column :urers, :id, :first => true
end
def self.down
remove_column :users, :id
end
end
Update the uniqueness field when it value dependent on another existent field without uniqueness restriction.
I'm using sub-transaction to update existent records on DB. I use this approach to update the uniqueness field when it value dependent on another existent field without uniqueness restriction.
==== Migration for uniqueness with existent dependent data in DB
class AddUniquenessBarToFoo < ActiveRecord::Migration
class Foo < ActiveRecord::Base
end
def change
add_column :foos, :bar, :string
execute "ALTER TABLE foos ADD CONSTRAINT uk_foods_bar UNIQUE (bar)"
Foo.reset_column_information
Foo.all.each do |f|
begin
#try get unique value in a new sub-transaction
Foo.transaction(requires_new: true) do
f.update_attributes!(:bar => "some ops. with another non-unique existent field to set this")
end
rescue ActiveRecord::StatementInvalid
#We can't reuse a crashed transaction. New one.
Foo.transaction(requires_new: true) do
#Alternative unique value, if another error exist it's another
#migration problem and then raise new error.
f.update_attributes!(:bar => "some operation to set this-#{f.id}")
end
end
end
change_column :foos, :bar, :string, :null => false
end
end
Be aware about performance that is transaction per record for big DB.