创建 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
end
migrate 一下以后,查看一下 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 字段的值。
e37f9e6a-313e-4dee-a66c-e41ec6e70ee8
77be23a6-5708-44cf-9fb3-303c23363755
3b599d49-65a2-4046-9dcd-9218b739798e
栏修饰符
在创建或修改栏的时候可以使用一些修饰符。
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
end
migrate 以后,查看一下 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) ==============================