Migration trong rails
Migration là một tính năng của Active record cho phép bạn thay đổi cả cấu trúc và dữ liệu trong database. Thay vì thay đổi trực tiếp vào database thì Rails cho phép bạn sử dụng Ruby DSL để mô tả việc thay đổi các table. Sau khi đọc xong bài này các bạn có thể biết: Active record là gì và cách ...
Migration là một tính năng của Active record cho phép bạn thay đổi cả cấu trúc và dữ liệu trong database. Thay vì thay đổi trực tiếp vào database thì Rails cho phép bạn sử dụng Ruby DSL để mô tả việc thay đổi các table. Sau khi đọc xong bài này các bạn có thể biết:
- Active record là gì và cách tạo
- Các method cung cấp trong Active Record để manipulate database
- Bin/rails task
- Schema
Migration mô tả cách thuận tiện để thay đổi cấu trúc bảng và dữ liệu trong DB một cách dễ dàng. Có thể hình dung mỗi migration chính là một version của database. Ban đầu, schema là rỗng, và mỗi lần migration thì sẽ modify để add hoặc remove table, columns hoặc rows. Active record biết cách để update schema theo thời gian. Và từ bất cứ thời điểm nào trong quá khứ cũng có thể update version của schema đến bản mới nhất. Active record cũng sẽ update file db/schema.rb để làm cho thống nhất với cấu trúc mới nhất của database. Ví dụ về migration
class CreateProducts < ActiveRecord::Migration def change create_table :products do |t| t.string :name t.text :description t.timestamps end end end
Nếu run đoạn migration bên trên thì sẽ tạo ra table products với 2 column name với kiểu string và description với kiểu text. Primary key id sẽ được tự động thêm vào, id là khóa chính default trong model active record. timestamps sẽ thêm vào trong table 2 column created_at và updated_at, và 2 cột này nếu tồn tại thì sẽ được quản lý tự động bởi active record. Trước khi thực hiện migration thì không tồn tại table nào cả. Run migration thì table sẽ được sinh ra. Và Active record cũng có cách để back lại cái migration lúc nãy bằng cách là Rollback lại cái migration đó thì bảng được tạo lúc trước sẽ bị xóa. Trong trường hợp không biết cách để rollback lại migration thì có thể dùng reversible
class ChangeProductsPrice < ActiveRecord::Migration def change reversible do |dir| change_table :products do |t| dir.up { t.change :price, :string } dir.down { t.change :price, :integer } end end end end
Thay vì dùng change thì cũng có thể dùng up & down
class ChangeProductsPrice < ActiveRecord::Migration def up change_table :products do |t| t.change :price, :string end end def down change_table :products do |t| t.change :price, :integer end end end
Create migration
Migration thì được lưu trong folder db/migrate và tương ứng với mỗi file thì sẽ là mỗi class migration. File migration sẽ có định dạng YYYYMMDDHHMMSS_create_products.rb. File name có kèm theo thời gian đề phân biệt các version migation. Và tên class phải thống nhất với tên file theo định dạng ví dụ 20080906120000_create_products.rb thì sẽ định nghĩa class CreateProducts.
$ bin/rails generate migration AddPartNumberToProducts
Command sẽ sinh ra migration với nội dung như bên dưới.
class AddPartNumberToProducts < ActiveRecord::Migration def change end end
Trong TH tên migration có định dạng "AddXXXToYYY" & "RemoveXXXFromYYY"., sau đó nếu nhập tiếp tên column thì migration sẽ tự động add thêm các trường add_colum và remove_column trong class. VD:
$ bin/rails generate migration AddPartNumberToProducts part_number:string
Thì class sinh ra bên dưới
class AddPartNumberToProducts < ActiveRecord::Migration def change add_column :products, :part_number, :string end end
Phần remove cũng tương tự. Còn trong TH bên file có định dạng CreateXXX, tiếp theo đó nhập tên và định nghĩa các column thì cũng sẽ được sinh ra bên trong class. Vd
$ bin/rails generate migration CreateProducts name:string part_number:string
Thì bên trong class sẽ là
class CreateProducts < ActiveRecord::Migration def change create_table :products do |t| t.string :name t.string :part_number end end end
Tạo model
$ bin/rails generate model Product name:string description:text
Đoạn command sẽ sinh ra migration như bên dưới
class CreateProducts < ActiveRecord::Migration def change create_table :products do |t| t.string :name t.text :description t.timestamps end end end
Run Active record
Trong rails có một số class Rake để chạy migration. Class rake run migration nhanh nhất trong hầu hết các trường hợp là bin/rails db:migrate. Thứ tự thực hiện migrate là dựa theo thời gian. Khi chạy command db:migrate thì task db:schema:dump cũng đồng thời được gọi ra, task này sẽ update file schema db/schema.rb và schema sẽ làm thống nhất cấu trúc của db. Nếu chỉ định version của migration thì active record sẽ thực hiện việc migration (change/up/down) đến version migation được chỉ định. Ví dụ, Trong trường hợp muốn migration đến version 20080906120000 thì chạy command như dạng bên dưới
$ bin/rails db:migrate VERSION=20080906120000
Rollback
Sẽ có nhiều trường hợp bạn muốn rollback lại version ngay trước. Ví dụ như trong trường hợp migration bị sai và muốn back lại. Thì trong trường hợp này cho dù không chỉ chính xác version của bản cần rollback thì chỉ cần run command bên dưới thì sẽ ok
$ bin/rails db:rollback
Trong TH muốn rollback lại nhiều version trước đó nữa thì chỉ định parameter step. Vd:
$ bin/rails db:rollback STEP=3
Command trên sẽ rollback lại 3 version cuối cùng. Setting database Task bin/rails db:setup thì sẽ create db, đọc schema và thực hiện các thiết lập đầu tiên của db sử dụng seed database. Reset database Task bin/rails db:reset sẽ drop db và setting lại db. Task này thì tương đương với task bin/rails db:drop db:setup. Chỉ run bản migration đặc định Trong trường hợp cần thiết phải up or down các migration đặc định thì sử dụng db:migrate:up hoặc db:migrate:down. Giống như bên dưới, chỉ cần chỉ định version thích hợp thì lần lượt từng method change, up, down bao gồm cả migration tương ứng.
$ bin/rails db:migrate:up VERSION=20080906120000
Run đoạn command bên trên thì method change nằm bên trong migration 20080906120000 sẽ được thực thi. Đầu tiên, nó sẽ check migration này đã được hoàn thành hay chưa. Trong trường hợp đã được Active record xác định đã hoàn thành thì sẽ k chạy gì cả.
Schema
Vì Rails migration không thật sự mạnh mẽ, nên việc tạo ra nguồn thông tin đủ tin tưởng để tạo schema của database là không phù hợp lắm. Khi deploy new instance của application thì không cần thiết phải thực hiện lại tất cả những bảng migration cũ. Nếu thực hiện thì rõ ràng là sẽ dễ phát sinh nhiều lỗi. Và đơn giản và nhanh chóng hơn là đọc vào database những cái đã ghi trong file schema hiện tại. File schema nó sẽ biết được ở object của Active record thì có những thuộc tính như thế nào, và thông tin schema thì không có trong code model, phần lớn được chia ra trong các bảng migration và tồn tại trong đó. Sử dụng gem annotate_models thì những comment thông tin tóm tắt schema sẽ được tự động add, update vào đầu của model file.
Các loại Schema dumps
Có 2 phương pháp để dump schema. Cách dump thì được setting trong file config/application.rb của config.active_record.schema_format. Chỉ định sql hoặc là :ruby Nếu chỉ định :ruby thì schema sẽ được lưu ở db/schema.rb. Mở thử file này có chắc chắn có thể nhìn thấy giống như là 1 bảng migration lớn.
ActiveRecord::Schema.define(version: 20080906171750) do create_table "authors", force: true do |t| t.string "name" t.datetime "created_at" t.datetime "updated_at" end create_table "products", force: true do |t| t.string "name" t.text "description" t.datetime "created_at" t.datetime "updated_at" t.string "part_number" end end
Cũng có trường hợp sử dụng migration để thêm data và database.
class AddInitialProducts < ActiveRecord::Migration def up 5.times do |i| Product.create(name: "Product ##{i}", description: "A product.") end end def down Product.delete_all end end
Tuy nhiên, ở rails thì có tính năng seed để mang data vào database lúc đầu tiên. Thêm code ruby vào file db/seeds.rb và run bin/rails db:seed thì sẽ có data ngay lập tức.
5.times do |i| Product.create(name: "Product ##{i}", description: "A product.") end