Một số lỗi thường gặp và cách giải quyết khi sử dụng migration

1. Giới thiệu

Khái niêm "Rails migrations" chắc hẳn không xa lạ gì đối với lập trình viên ruby on rails nói riêng và tất cả lập trình viên của các ngôn ngữ khác nói chung. Việc thao tác với nó là rất phổ biến, và việc gặp lỗi cũng phổ biến không kém. Ở bài viết này, mình sẽ liệt kê ra 1 số lỗi thông dụng và phương pháp giải quyết nó nhé.

2. Các lỗi thường gặp và phương pháp giải quyết

2.1 Rollback

Câu lệnh

rake db:rollback 

sử dụng khá phổ biến cho việc revert lại db khi gặp lỗi. Nhưng có một số trường hợp không thể revert lại db nên chúng ta không thể sử dụng câu lệnh này ví dụ như trường hợp này:

class DropUsers < ActiveRecord::Migration
  def change
    drop_table :users
  end
end

Ở trường hợp này, khi ta rake db:migrate và sau đó rake db:rollback thì sẽ gây ra lỗi. vì vậy sử dụng up/down để thay thế cho change, và chỉ định rõ là khi revert sẽ raise ra lỗi fail ActiveRecord::IrreversibleMigration

class DropUsers < ActiveRecord::Migration
  def up
    drop_table :users
  end

  def down
    fail ActiveRecord::IrreversibleMigration
  end
end

Hoặc nếu muốn revert trong trường hợp này, chúng ta phải chỉ định rõ bảng user gồm những gì, và chúng ra drop nó sẽ bao gôm những gì, thì mới có thể revert lại được

class DropUsers < ActiveRecord::Migration
  def change
    drop_table :users do |t|
      t.string :email, null: false
      t.timestamps null: false
    end
  end
end

2.2 add_column

a. Adding a foreign key?

Khi viết mối quan hệ giữa các bảng với nhau cỡ db, cú pháp phổ biến:

add_column :articles, :author_id, :integer
add_index :articles, :author_id
add_foreign_key :articles, :authors

nhưng chúng ta nên sử dụng add_reference (hoặc add_belongs_to) để thay thế cho add_foreign_key

# good
add_reference :articles, :author, index: true, foreign_key: true
# also good
add_belongs_to :articles, :author, index: true, foreign_key: true
b. Thêm ràng buộc not null vào các column

Khi 1 table có 1 column đòi hỏi không được đẻ trống, chúng ta thường không viết nó ở db, mà chỉ validate ở model:

add_column :users, :email, :string

cách viết tốt hơn

add_column :users, :email, :string, null: false

Và tất nhiên, không phải chúng ta chỉ viết null: false cỡ db như này là đã đủ, mà vẫn cần validate đối với model

# GOOD
add_column :users, :email, :string, null: false
# ...
class User < ActiveRecord::Base
  validates :email, presence: true
end
c. Thêm ràng buộc uniq

Tương tự như trên, đối với uniq chúng ta cũng cần viết nó ở file migration cách viết không tốt:

add_column :users, :email, :string

cách viết tốt hơn:

add_column :users, :email, :string
add_index :users, :email, unique: true
d. Thêm giá trị default

Khi tạo ra 1 record của 1 bảng nào đó có column đòi hỏi giá trị defautl, thì chúng ta cũng cần phải viết nó trong file migration

add_column :users, :status, :boolean, null: false, default: true
e. Đánh index cho column của bảng giúp tăng tốc độ query

Đều này thì với những table có lượng records lớn chúng ta sẽ thấy rõ sự khác biệt.

# BAD
add_column :users, :email, :string
# Good
add_column :users, :email, :string
add_index :users, :email

2.3 change_column_null

giả sử ta có 1 table user với column favorite_color, lúc đầu tiên ta tạo các record có favorite_color đều là null, và giờ ta muốn add điểu kiện là column này phải not null. Nếu sử dụng

change_column :users, :favorite_color, :string, null: false

Cách viết này có thể gây ra lỗi vì trước đó đã có rất nhiều record có favorite_color là null bởi vậy cách viết đúng phải là

# GOOD - thay đổi tất cả các record có clumn này nil về 'blue' sau đó thì add điều kiện null: false
change_column_null :users, :favorite_color, true, 'blue'

2.4 remove_column

Cách thương dùng

def change
  remove_column :posts, :slug
end

Nhưng khi reverts rake db:rollback thì sẽ bị báo lỗi. Cách viết đúng

# GOOD
def change
  remove_column :posts, :slug, :string, null: false, default: ''
end

2.5 create_table

Khi tạo bảng cần phải thêm timestamps (created_at, updated_at)

# BAD - no timestamp
create_table :users  do |t|
  t.string :email, null: false
end
create_table :users  do |t|
  t.string :email, null: false
  t.timestamps null: false
end

3. Kết Luận

Trên đây mình đã trình bày một số lỗi thường gặp khi tạo file migration, và các cú pháp để viết nó một cách tốt nhất. Hi vọng rằng, qua bài viết này, các bạn sẽ không phải những lỗi ở trên nữa, và tạo ra những sản phẩm chất lượng hơn.

4. Tài liệu tham khảo

https://maxwellholder.com/blog/rails-migration-checklist/