Tìm hiểu về Migration

Migration là tính năng quan trọng của Active Record trong Rails, cho phép bạn thay đổi cấu trúc và dữ liệu trong database một cách linh hoạt. Thay vì chỉnh sửa trực tiếp trên database, Rails cung cấp DSL (Domain Specific Language) dễ dùng để mô tả các thay đổi trên các bảng. Trên thực tế, việc sử dụng migration là đặc biệt hữu ích khi bạn muốn quay trở lại một phiên bản cũ hơn của code mà không cần thay đổi code hiện tại.

Trong bài viết này, chúng ta sẽ tìm hiểu thêm về các khái niệm sau:

1. Tổng quan về Migration

Migration là một cách thuận tiện để thay đổi cấu trúc bảng và dữ liệu trong database một cách dễ dàng. Bằng cách sử dụng DSL của Ruby, bạn không cần phải viết SQL bằng tay, giúp thay đổi database một cách độc lập. Mỗi migration tương ứng với một phiên bản của database. Khi thực hiện migration, Rails sẽ thay đổi schema theo thời gian. Bất kỳ thời điểm nào trong quá khứ cũng có thể cập nhật phiên bản schema đến bản mới nhất. Ngoài ra, Rails cũng tự động cập nhật file db/schema.rb để phù hợp với cấu trúc mới nhất của database.

Ví dụ, dưới đây là một migration tạo bảng products trong database:

class CreateProducts < ActiveRecord::Migration[5.0]
  def change
    create_table :products do |t|
      t.string :name
      t.text :description
      t.timestamps
    end
  end
end

Trên đây là một số điểm cần lưu ý:

  • Bảng products sẽ có hai trường namedescription.
  • Cột id sẽ được tự động thêm vào bảng sau khi thực hiện migration. Đây là khóa chính mặc định cho tất cả các model trong Active Record.
  • Cột timestamps sẽ thêm vào bảng hai trường created_atupdated_at. Những trường này sẽ được quản lý tự động bởi Active Record nếu tồn tại.

Để hiểu cách rollback, ta có thể viết một migration như sau:

class ChangeProductsPrice < ActiveRecord::Migration[5.0]
  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
  • dir.up được sử dụng để chạy migration, trong khi dir.down được sử dụng để rollback, quay về thời điểm trước khi chạy migration.

2. Tạo migration

2.1 Tạo migration

Migration được lưu trữ trong các file trong thư mục db/migrate. Tên của file migration sẽ tuân thủ theo định dạng YYYYMMDDHHMMSS_create_products.rb. Rails sử dụng dấu thời gian này để xác định thứ tự chạy migration. Nếu bạn sao chép migration từ một ứng dụng khác hoặc tạo ra một file mới, hãy chắc chắn biết vị trí của nó trong thứ tự.

Để tạo migration mới, Rails cung cấp một cú pháp dễ dùng:

$ bin/rails generate migration AddPartNumberToProducts

Cú pháp này sẽ tạo một migration mới có tên là AddPartNumberToProducts, ví dụ:

class AddPartNumberToProducts < ActiveRecord::Migration[5.0]
  def change
  end
end

Nếu tên migration có định dạng AddXXXToYYY hoặc RemoveXXXFromYYY, Rails sẽ tự động tạo các migration add_column hoặc remove_column. Bạn có thể thêm các cột và kiểu của chúng trong command:

$ bin/rails generate migration AddPartNumberToProducts part_number:string

Sẽ tạo ra migration như sau:

class AddPartNumberToProducts < ActiveRecord::Migration[5.0]
  def change
    add_column :products, :part_number, :string
  end
end

Migration với tên dạng CreateXXX và danh sách các cột và kiểu của chúng sẽ tạo ra một bảng có tên XXX với các cột đã liệt kê. Ví dụ:

$ bin/rails generate migration CreateProducts name:string part_number:string

Sẽ tạo ra migration:

class CreateProducts < ActiveRecord::Migration[5.0]
  def change
    create_table :products do |t|
      t.string :name
      t.string :part_number
    end
  end
end

2.2 Tạo model

Khi tạo model, bạn có thể tạo migration tương ứng. Ví dụ, để tạo một model mới có tên là Product:

$ bin/rails generate model Product name:string description:text

Câu lệnh trên sẽ tạo một migration mới như sau:

class CreateProducts < ActiveRecord::Migration[5.0]
  def change
    create_table :products do |t|
      t.string :name
      t.text :description
      t.timestamps
    end
  end
end

Bạn có thể thêm nhiều cột khác nếu cần.

3. Chạy migration

Rails cung cấp một tập hợp các task để chạy migration. Câu lệnh hay được sử dụng nhất là rails db:migrate, nó sẽ chạy các migration chưa được chạy. Nó sẽ chạy theo thứ tự thời gian. Khi chạy db:migrate, Rails cũng tự động chạy db:schema:dump để cập nhật file db/schema.rb với cấu trúc hiện tại của database.

Nếu bạn muốn chạy một migration cụ thể, bạn có thể sử dụng tên version của migration và chạy lệnh:

$ bin/rails db:migrate VERSION=20080906120000

Nếu version 20080906120000 lớn hơn version hiện tại, nó sẽ chạy các thay đổi (up method) của tất cả các migration, bao gồm cả migration đó, và không chạy migration nào sau đó.

3.1 Rolling back

Rollback được sử dụng khi bạn muốn sửa lỗi trong một migration hoặc muốn quay trở lại một phiên bản cũ hơn. Bạn có thể rollback migration trước đó bằng cách chạy lệnh:

$ bin/rails db:rollback

Lệnh trên sẽ rollback migration mới nhất. Nếu bạn muốn rollback nhiều migration, bạn có thể sử dụng tham số STEP. Ví dụ:

$ bin/rails db:rollback STEP=3

Lệnh trên sẽ rollback 3 migration gần nhất. Bạn cũng có thể sử dụng db:migrate:redo để chạy rollback và migration lại:

$ bin/rails db:migrate:redo STEP=3

Các lệnh trên giúp sửa lỗi các migration mà không cần phải reset migration.

Một lưu ý khi tạo migration là nên kiểm tra xem nó có thể rollback được hay không. Có thể sẽ không rollback được do nhiều lý do, ví dụ như change_column không rollback được hoặc khi thêm điều kiện để chạy migration.

3.2 Cài đặt database

Bạn có thể tạo database và cập nhật schema sử dụng lệnh db:setup:

$ bin/rails db:setup

3.3 Reset database

Để xóa database và cài đặt lại nó, bạn có thể sử dụng lệnh db:reset, tương đương với db:dropdb:setup:

$ bin/rails db:reset

3.4 Chạy một migration bất kỳ

Nếu bạn muốn chạy một migration bất kỳ với up hoặc down, bạn có thể sử dụng db:migrate:updb:migrate:down. Đặt version của migration sau lệnh để xác định migration cần chạy:

$ bin/rails db:migrate:up VERSION=20080906120000

Lệnh trên sẽ chạy migration với version 20080906120000 và các thay đổi (up method). Nếu migration này đã được chạy trước đó, nó sẽ không chạy lại.

3.5 Chạy migration trên các môi trường khác nhau

Mặc định, db:migrate sẽ chạy trên môi trường development. Nếu bạn muốn chạy trên các môi trường khác, hãy thêm biến môi trường RAILS_ENV vào sau lệnh. Ví dụ, để chạy migration trên môi trường test:

$ bin/rails db:migrate RAILS_ENV=test

4. Schema

4.1 Mục đích

File schema cho phép bạn biết các thuộc tính của đối tượng Active Record. Những thông tin này không có trong code model mà được tạo ra thông qua migration.

4.2 Các loại Schema Dump

Rails cung cấp hai phương pháp để dump schema. Cách dump này được định cấu hình trong file config/application.rb, trong mục config.active_record.schema_format. Bạn có thể chỉ định :sql hoặc :ruby. Nếu chỉ định :ruby, schema sẽ được lưu trong file db/schema.rb. Mở file này, bạn có thể thấy nó gần giống như một 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

5. Migration và Seed data

Migration có thể được sử dụng để thêm hoặc chỉnh sửa dữ liệu. Điều này rất hữu ích khi bạn đã có sẵn một database và không thể xóa hoặc tạo lại nó, chẳng hạn như production database. Dưới đây là một ví dụ:

class AddInitialProducts < ActiveRecord::Migration[5.0]
  def up
    5.times do |i|
      Product.create(name: "Product ##{i}", description: "A product.")
    end
  end

  def down
    Product.delete_all
  end
end

Để thêm dữ liệu sau khi tạo database, Rails cung cấp tính năng db:seed. Bằng cách thêm code vào file db/seeds.rb và chạy lệnh rails db:seed, bạn có thể thêm dữ liệu nhanh chóng:

5.times do |i|
  Product.create(name: "Product ##{i}", description: "A product.")
end

Hy vọng bài viết này giúp bạn hiểu rõ hơn về migration và sử dụng nó trong Rails!

Nguồn tham khảo: http://edgeguides.rubyonrails.org/active_record_migrations.html#changing-existing-migrations

FEATURED TOPIC