Rails Active Record Migration的例子

作者:袖梨 2022-06-29

创建 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) ==============================

相关文章

精彩推荐