- 任何对数据库的操作(改变表的结构),必须使用migration
- 创建表
- 修改表
- 删除表
- 新增列
- 修改列
- 删除列
- migration 一旦创建好,并且上传到了远程服务器,就绝对不能做改动。
创建
例如:我想新建一个表
users
, 它有个属性:name
,age
就通过
rails generate migration
命令创建:$ bundle exec rails g migration create_users invoke active_record create db/migrate/20160308125025_create_users.rb
可以看到,上面的命令, 建立了文件:
db/migrate/20160308125025_create_users.rb
打开这个文件, 并且编辑它的内容, 如下:
class CreateUsers < ActiveRecord::Migration def up # 建立 users 表 create_table :users do |t| # 建立列: name, 类型是string t.string :name # 建立列: age, 类型是 integer t.integer :age # 建立created_at 与 updated_at , 类型都是 datetime t.timestamps end end def down # 删掉 users 表 drop_table :users end end
up/down 与 change的区别.
(如果你用rails 4.x 来创建的话,得到的migration ,一般没有up, down 方法。因为,rails 非常智能,能自动的,把 up, down方法合并成: change
例如,如果up
方法中,是create_table
, 那么,rails就会自动判断出,在down
方法中,就用drop_table
。
但是,还是有些时刻,rails无法自动判断up/down
, 例如: 改变某个列的类型。这个时候,还的 使用经典的 up/down
方法。
对于新手, 建议使用 up/down
方法.免得被 change
弄糊涂.)
运行 rake db:migrate
接下来, 运行 rake db:migrate
:
$ bundle exec rake db:migrate
== 20160308125025 CreateUsers: migrating ======================================
-- create_table(:users)
-> 0.0078s
== 20160308125025 CreateUsers: migrated (0.0079s) =============================
我们打开这个数据库,发现数据库中,新增了一个表 users
.
sqlite> .tables
schema_migrations users
回滚
回滚,就是通过一条命令,
rake db:rollback
来执行对应的migration中的down
方法。$ bundle exec rake db:rollback == 20160308125025 CreateUsers: reverting ====================================== -- drop_table(:users) -> 0.0012s == 20160308125025 CreateUsers: reverted (0.0068s) =============================
可以看到,table中就少了:
users
sqlite> .tables schema_migrations
下面,是另一个完整的例子,可以看到 down 中的方法,与 up 是相反的:
class RenameAgeToNianLingFromUsers < ActiveRecord::Migration def up rename_column :users, :age , :nian_ling end def down rename_column :users, :nian_ling, :age end end
schema_migrations: 记录迁移过程的表
可能有的同学会比较奇怪:
schema_migrations
, 这个是干嘛的呢?这个表专门记录当前数据库的 "迁移ID" 是多少。 Rails就是通过比较它和
db/migrate
中文件的差异来判断, 当前的rails,的数据库,是否是最新的。例如, 运行
CreateUsers
这个migration之前:sqlite> select * from schema_migrations; 20151023070737
运行完之后:
sqlite> select * from schema_migrations; 20151023070737 20160308125025
多出来的一行:
20160308125025
, 刚好就是我们新建的migration :20160308125025_create_users
名字的一部分。(一旦某个rails的正式项目,开始之后,这个表的内容会很多)
如何修改一个列?
错误的做法:
- 运行回滚操作:
rake db:rollback
- 修改migration文件的内容。 在其中增加
change_column
方法. (具体代码略)$ rake db:migrate
绝对错误!记住: 一旦创建好migration文件(特别是已经提交到了远程的话),就绝对不要去修改它!
因为一rollback的话,迁移的时间线就立马乱了。
正确的做法是:
- 新建个migration. (在这个migration中,使用 change_column 方法,来修改)
- 运行它
例子
例如,我想把 age 列,改名字,改成: nian_ling, 我应该:
1.运行命令:
$ bundle exec rails g migration rename_age_to_nian_ling_from_users_table
这里的migration的命名(在上面,就是
rename_age_to_nian_ling_from_users_table
),不是特别严格的。不会引起错误。但是原则上,migration 要看到名字, 就能知道它是做什么的。 例如:
- create_users: 创建users 表
- remove_name_from_users: 从users表中删掉 name列.
然后得到结果.
$ bundle exec rails g migration rename_age_to_nian_ling_from_users invoke active_record create db/migrate/20160308130535_rename_age_to_nian_ling_from_users.rb
2.编辑上面命令,产生的文件。
# db/migrate/20160308130535_rename_age_to_nian_ling_from_users.rb class RenameAgeToNianLingFromUsers < ActiveRecord::Migration def change # 手动增加下面这行代码: rename_column :users, :age, :nian_ling end end
3.运行
rake db:migrate
$ bundle exec rake db:migrate == 20160308130535 RenameAgeToNianLingFromUsers: migrating ===================== -- rename_column(:users, :age, :nian_ling) -> 0.0111s == 20160308130535 RenameAgeToNianLingFromUsers: migrated (0.0112s) ============
就能够看到,schema_migrations 表中,增加了一条记录, 就是我们刚才运行的migration的时间戳.
sqlite> select * from schema_migrations; 20151023070737 20160308125025 20160308130535
并且,
users
表中,age
列变成了nian_ling
列。sqlite> .schema ... CREATE TABLE "users" ( "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar(255), "nian_ling" integer, "created_at" datetime, "updated_at" datetime ); sqlite>
常见的migration方法
注: 以下方法都写在
up
,down
或者change
方法中.
- create_table
例如, 创建表 'students' :
create_table :students do |t| t.string :chinese_name t.integer :age t.timestamps end
上面的
t.timestamps
, 会创建两个列:created_at
,updated_at
.
- created_at: 表示 该条记录,在什么时间被创建的。
- updated_at: 表示 该条记录,在什么时间被修改的。
等同于:
t.datetime :created_at t.datetime :updated_at
rails中几乎每个表,都默认有这两个列。
- drop_table
例如,删掉表'students' :
drop_table :students
- add_column
例如, 向'students' 表中,增加一个列'name', 它的类型是字符串:
add_column :students, :name, :string
- remove_column
例如, 从'students' 表中,删除列'name':
remove_column :students, :name
- rename_column
例如, 把'students' 表的'chinese_name'列, 重命名为 'zhong_wen_ming_zi':
rename_column :students, :chinese_name, :zhong_wen_ming_zi
- add_index
例如, 把'students'表的name列建立索引:
add_index :students, :name
- remove_index
例如, 把'students'表的已经存在的name索引删掉:
remove_index :students, :name
几个注意:
- 所有的model , 都是单数。
- 所有的controller, 都是复数(原则上)
- 所有的table, 都是复数。(例如: egg => eggs, person => people, wife -> wives)
如果弄错了,rails的代码在默认配置下就会出错。
不学的内容
不要这样写, 容易乱:
def change create_table :appointments do |t| t.belongs_to :physician t.belongs_to :patient t.datetime :appointment_date t.timestamps end end
可以这样写(就把外键当成最普通的列就行了):
class CreateAppointments < ActiveRecord::Migration def change create_table :appointments do |t| t.integer :physician_id t.integer :patient_id t.datetime :appointment_date t.timestamps end end end