宁皓网的付费会员可以查看课程:《Rails:数据库 Migration》http://ninghao.net/course/4133
创建 Migration
使用命令行:
rails generate migration 名字
简写:
rails g migration 名字
migration 的名字要描述清楚它的作用,使用驼峰式的名字,创建的 migration 会包含一个时间,它会作为数据库的版本,Rails 也会用这个时间来判断哪些是执行过的 migration 。执行的 migration 会记录在数据库里。
现在我要创建一个 migration,可以帮助我们在数据库里创建一个数据表:
rails generate migration CreateArticles
返回的东西像这样:
Running via Spring preloader in process 607 invoke active_record create db/migrate/20160927082426_create_articles.rb
migration 文件会保存在 db/migrate 目录的下面,文件的名字里 20160927082426 是创建这个 migration 的时间。打开这个 migration:
class CreateArticles < ActiveRecord::Migration[5.0]
  def change
    create_table :articles do |t|
    end
  end
end一个 migration 就是一个类,默认它里面定义了一个 change 方法,在这个方法里,这个 migration 使用了 create_table 方法,创建了一个名字是 :articles 的数据表。rails 根据我们在创建这个 migration 的时候使用的名字推断我们是要创建一个可以创建名字是 articles 数据表的 migration。
运行 Migration
使用命令:
rails db:migrate
输出:
== 20160927082426 CreateArticles: migrating =================================== -- create_table(:articles) -> 3.7057s == 20160927082426 CreateArticles: migrated (3.7066s) ==========================
这样就会真正的执行在 migration 里定义的动作。我们这里就是去在数据库里创建一个名字是 articles 的数据表。
SHOW TABLES; +---------------------------+ | Tables_in_app_development | +---------------------------+ | ar_internal_metadata | | articles | | schema_migrations | +---------------------------+
查看一下这个表:
DESCRIBE articles; +-------+---------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------+---------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | +-------+---------+------+-----+---------+----------------+
新创建的这个数据表里面只有一个 id 字段,它是这个表的主键。
schema_migrations
再查看一下 schema_migrations 表:
SELECT * FROM `schema_migrations`; +----------------+ | version | +----------------+ | 20160927082426 | +----------------+
这个表只有一个 version,它表示数据库的版本,字段的值就是 migration 文件名字里的那个时间部分。这个表会记录已经执行了的 migration。
回滚 Migration
Rollback,回滚。它有点像是 migrate 的逆向操作。回滚最近一次做的 migrate,执行:
rails db:rollback
输出:
== 20160927084920 AddTitleToArticles: reverting =============================== -- remove_column(:articles, :title, :string) -> 5.5738s == 20160927084920 AddTitleToArticles: reverted (5.5813s) ======================
在输出的内容里,你会发现,执行了 remove_column 方法,它可以删除表里的字段,这里它把 articles 表里的 title 字段给删除掉了。add_column 的逆向操作就是 remove_column。
再查看一下 articles 表,你会发现之前添加的 title 字段已经不见了。在执行这个命令的时候可以加上一个 STEP 参数,它可以指定要回滚的次数。也就是去回滚最近几次做的 migrate 。
rails db:rollback STEP=3
redo
redo 就是先 rollback,然后再 migrate 一下。
rails db:migrate:redo
添加栏
再创建一个 migration,可以在 articles 表里添加一个 title 字段:
rails generate migration AddTitleToArticles title:string
输出:
Running via Spring preloader in process 625
      invoke  active_record
      create    db/migrate/20160927084920_add_title_to_articles.rb生成的 migration 是:
class AddTitleToArticles < ActiveRecord::Migration[5.0]
  def change
    add_column :articles, :title, :string
  end
end
change 方法里用了 add_column 方法,它可以在指定的数据表里添加字段,Rails 已经为我们设置好了数据表的名字。
执行:
rails db:migrate
输出:
== 20160927084920 AddTitleToArticles: migrating =============================== -- add_column(:articles, :title, :string) -> 4.7404s == 20160927084920 AddTitleToArticles: migrated (4.7405s) ======================
查看数据库里的 articles 数据表:
DESCRIBE articles; +-------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------+--------------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | title | varchar(255) | YES | | NULL | | +-------+--------------+------+-----+---------+----------------+
表里有了新字段,名字是 title,类型是 varchar(string)。
再查看一下 schema_migrations 表里的记录:
SELECT * FROM schema_migrations`; +----------------+ | version | +----------------+ | 20160927082426 | | 20160927084920 | +----------------+
多了一条:20160927084920,它就是刚才我们添加了用来在 articles 表里添加 title 字段的那个 migration 的文件名字里的时间部分。
重命名表
创建一个可以重命名表的 migration:
rails generate migration RenameArticlesToPosts
输出:
Running via Spring preloader in process 701
      invoke  active_record
      create    db/migrate/20160927101449_rename_articles_to_posts.rb手工编辑一下生成的这个 migration:
class RenameArticlesToPosts < ActiveRecord::Migration[5.0]
  def change
    rename_table :articles, :posts
  end
end
rename_table 方法可以重命名数据表。
执行:
rails db:migrate
返回:
== 20160927101449 RenameArticlesToPosts: migrating ============================ -- rename_table(:articles, :posts) -> 0.5636s == 20160927101449 RenameArticlesToPosts: migrated (0.5637s) ===================
查看:
SHOW TABLES; +---------------------------+ | Tables_in_app_development | +---------------------------+ | ar_internal_metadata | | posts | | schema_migrations | +---------------------------+
之前的 articles 表的名字,已经变成了 posts。
修改栏
先创建一个 migration,在 posts 表里添加一个新的字段 .. 名字是 content ,类型是 text。
rails generate migration AddContentToPosts content:text
migrate:
rails db:migrate
现在我们的 posts 表是:
DESCRIBE posts; +---------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +---------+--------------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | title | varchar(255) | YES | | NULL | | | content | text | YES | | NULL | | +---------+--------------+------+-----+---------+----------------+
我要修改一下 content 字段的类型,创建一个 migration:
rails generate migration ChangeContentFromPosts
打开这个新创建的 migration,编辑一下:
class ChangeContentFromPosts < ActiveRecord::Migration[5.0]
  def change
    change_column :posts, :content, :string
  end
end
修改字段用的是 change_column 方法,把 posts 表里的 content 字段的类型修改成了 string。
migrate:
rails db:migrate
输出:
== 20160927105001 ChangeContentFromPosts: migrating =========================== -- change_column(:posts, :content, :string) -> 5.7436s == 20160927105001 ChangeContentFromPosts: migrated (5.7438s) ==================
查看:
DESCRIBE posts; +---------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +---------+--------------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | title | varchar(255) | YES | | NULL | | | content | varchar(255) | YES | | NULL | | +---------+--------------+------+-----+---------+----------------+
观察 content 字段的类型的变化。
up/down
This migration uses change_column, 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.
修改表
change_table 可以修改数据表里的字段。创建一个 migration 用一下这个方法:
rails generate migration ChangeDetailsFromPosts
修改:
class ChangeDetailsFromPosts < ActiveRecord::Migration[5.0]
  def change
    change_table :posts do |t|
      t.rename :content, :body
      t.timestamps
    end
  end
end
在 change 方法里我们用了 change_table,修改了 posts 表里的一些东西,把 content 栏重命名成了 body,又添加了一个 timestamps,它会生成两个栏,created_at 与 updated_at 。
DESCRIBE posts; +------------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +------------+--------------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | title | varchar(255) | YES | | NULL | | | body | varchar(255) | YES | | NULL | | | created_at | datetime | NO | | NULL | | | updated_at | datetime | NO | | NULL | | +------------+--------------+------+-----+---------+----------------+
创建表
create_table 可以在数据库里创建数据表,创建一个数据表,名字是 users:
rails generate migration CreateUsers
创建的这个 migration 是这样的:
class CreateUsers < ActiveRecord::Migration[5.0]
  def change
    create_table :users do |t|
    end
  end
end自动在 migration 里使用了 create_table 方法,修改一下这个 migration:
class CreateUsers < ActiveRecord::Migration[5.0]
  def change
    create_table :users do |t|
      t.string :user_name
      t.string :email
      t.timestamps
    end
  end
endmigrate 一下以后,查看一下 users 数据表:
DESCRIBE users; +------------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +------------+--------------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | user_name | varchar(255) | YES | | NULL | | | email | varchar(255) | YES | | NULL | | | created_at | datetime | NO | | NULL | | | updated_at | datetime | NO | | NULL | | +------------+--------------+------+-----+---------+----------------+
添加关联
在 posts 表上添加一个到 user 的关联。
rails generate migration AddUserRefToPosts user:references
Running via Spring preloader in process 91
      invoke  active_record
      create    db/migrate/20160928005804_add_user_ref_to_posts.rb这条命令里我们添加了一个 user:references,它会给我们在 posts 表里生成一个到 user 的关联。
class AddUserRefToPosts < ActiveRecord::Migration[5.0]
  def change
    add_reference :posts, :user, foreign_key: true
  end
end这个 migration 里用了 add_reference,在 posts 表上添加一个到 user 的关联。foreign_key 设置了外键。
DESCRIBE posts; +------------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +------------+--------------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | title | varchar(255) | YES | | NULL | | | body | varchar(255) | YES | | NULL | | | created_at | datetime | NO | | NULL | | | updated_at | datetime | NO | | NULL | | | user_id | int(11) | YES | MUL | NULL | | +------------+--------------+------+-----+---------+----------------+
migrate 一下。你可以使用 phpMyAdmin 或者 MySQL Workbench 去查看表之间的关联。你会发现,user_id 是 posts 表里的一个外键,它对应的是同数据库里的 users 表里的 id 字段的值。



栏修饰符
在创建或修改栏的时候可以使用一些修饰符。
- limit
- precision
- scale
- polymorphic
- null
- default
- index
- comment
关键词:“ rails column modifiers ”
比如现在我想修改 users 表里的 email 栏的长度,而且想给它添加一个默认的值。可以使用 change_column 方法,再加上 limit 这个修饰符。
创建一个 migration:
rails generate migration AlterEmailFromUsers
Running via Spring preloader in process 133
      invoke  active_record
      create    db/migrate/20160928015618_alter_email_from_users.rb修改一下这个 migration:
class AlterEmailFromUsers < ActiveRecord::Migration[5.0]
  def change
    change_column :users, :email, :string, limit: 100
  end
endmigrate 以后,查看一下 users 表:
DESCRIBE users; +------------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +------------+--------------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | user_name | varchar(255) | YES | | NULL | | | email | varchar(100) | YES | | NULL | | | created_at | datetime | NO | | NULL | | | updated_at | datetime | NO | | NULL | | +------------+--------------+------+-----+---------+----------------+
注意 email 栏的长度的变化。
change
- add_column
- add_foreign_key
- add_index
- add_reference
- add_timestamps
- change_column_default(要提供- :from与- :to选项)
- change_column_null
- create_join_table
- create_table
- disable_extension
- drop_join_table
- drop_table(必须提供一个代码块)
- enable_extension
- remove_column(必须提供类型)
- remove_foreign_key(必须提供第二个表)
- remove_index
- remove_reference
- remove_timestamps
- rename_column
- rename_index
- rename_table
reversible
自己定义 migrate 的时候执行的动作,还有对应的 rollback 的时候要执行的动作。
rails generate migration ReversibleDemo
修改:
class ReversibleDemo < ActiveRecord::Migration[5.0]
  def change
    reversible do |dir|
      dir.up do
        say "前进!"
      end
      dir.down do
        say "撤退!"
      end
    end
  end
end
dir 表示方向,up 就是在 migrate 的时候执行的动作,down 就是在 rollback 的时候执行的动作。
migrate:
rails db:migrate == 20160928023555 ReversibleDemo: migrating =================================== -- 前进! == 20160928023555 ReversibleDemo: migrated (0.0002s) ==========================
rollback:
rails db:rollback == 20160928023555 ReversibleDemo: reverting =================================== -- 撤退! == 20160928023555 ReversibleDemo: reverted (0.0284s) ==========================
revert
恢复之前执行过的 migration。
rails generate migration RevertDemo
可以使用 revert,给它一个代码块:
class RevertDemo < ActiveRecord::Migration[5.0]
  def change
    revert do
      # 要恢复的动作
    end
    say "重做!"
  end
end也可以导入之前创建的 migration:
require_relative '20160928023555_reversible_demo'
class RevertDemo < ActiveRecord::Migration[5.0]
  def change
    revert ReversibleDemo
    say "重做!"
  end
end执行:
rails db:migrate == 20160928034734 RevertDemo: migrating ======================================= -- 撤退! -- 重做! == 20160928034734 RevertDemo: migrated (0.0073s) ==============================
Ruby




评论
路过。。。
9 年 3 周 以前
文章第一行就有错别字。皓哥太大意了哈哈。
9 年 3 周 以前
嘿嘿嘿,见笑了哈。
9 年 3 周 以前