🦄 2024 独立开发者训练营,一起创业!(早鸟优惠在6天后结束)查看介绍 / 立即报名 →

Day 16:Rails Active Record Migration

宁皓网的付费会员可以查看课程:《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
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) ==============================

 

 

Ruby

评论

路过。。。

文章第一行就有错别字。皓哥太大意了哈哈。

嘿嘿嘿,见笑了哈。

微信好友

用微信扫描二维码,
加我好友。

微信公众号

用微信扫描二维码,
订阅宁皓网公众号。

240746680

用 QQ 扫描二维码,
加入宁皓网 QQ 群。

统计

14696
分钟
0
你学会了
0%
完成

社会化网络

关于

微信订阅号

扫描微信二维码关注宁皓网,每天进步一点