+1

Requiring Uniqueness in Rails

Mở đầu

How to ensure your values are unique

Thường trong các trường hợp bạn muốn đảm bảo rằng các dữ liệu của bạn là độc nhất không có sự trùng lặp. Bạn chỉ muốn một địa chỉ email được sử dụng 1 lần... Nếu không thì điều gì sẽ xảy ra khi người dùng đăng nhập vào hệ thống.

Trong một số trường hợp khác nữa, ví dụ như khi spec có yêu cầu làm thẻ quà tặng. Bạn chỉ muốn rằng số giftcard hoặc mã phiếu giảm giá sẽ được sử dụng một lần duy nhất .

Rails có một cách dễ dàng để xử lý vấn đề này, nhưng vấn đề ở đây là nó chưa đủ. Tiếp đây tôi sẽ giải thích lý do tại sao.

Nội dung

Validating for uniqueness

Rails có một validator tiện dụng để đảm bảo rằng các bản ghi lưu vào DB là duy nhất. Chúng ta chỉ cần viết đoạn code đơn giản như sau: validates :field, uniqueness: true Và chức năng này sẽ làm hết sức để có thể đảm bảo rằng giá trị trong trường được validate là duy nhất.

Bạn cũng có thể thêm tùy chọn: case_sensitive: false, để đảm bảo rằng nó sẽ không cho phép EMAIL@gmail.com được lưu vào DB nếu email@gmail.com đã tồn tại trong DB. Với trường hợp là Postgres thì bạn cần phải chú ý điều này nếu muốn đảm bảo tính duy nhất của trường được set unique.

Hoặc bạn có thể downcase giá trị trước khi lưu bó vào DB, đó là một lựa chọn hoàn toàn hợp lý. Nhưng không phải cái gì cũng downcase được, ví như cái mã code, giftcard cần phân biệt hoa thường.

class Brand < ActiveRecord::Base
  validates :slug, uniqueness: true
  validates :name, uniqueness: {case_sensitive: false}
end

This isn't enough!

Lý do tại sao thì chúng ta cùng xem xét cách hoạt động validate uniqueness của rails.

Khi bạn cố gắng lưu 1 bản ghi vào DB có các bước sau xảy ra:

  1. Query DB để xem giá trị này đã tồn tại trong DB hay chưa?
  2. Nếu có một bản ghi, validation trả về false.
  3. Nếu không có bản ghi nào, validation trả về true.

Vấn đề xảy ra ở đây là khi hệ thống của bạn có 2 người cùng 1 lúc cố gắng để tạo ra 1 tài khoản người dùng với cùng 1 địa chỉ email.

  1. User-1: Query DB để xem giá trị này đã tồn tại trong DB hay chưa?
  2. User-2: Query DB để xem giá trị này đã tồn tại trong DB hay chưa?
  3. User-1: Ok, giá trị không tồn tại, tiến hành insert bản ghi mới.
  4. User-2: Ok, giá trị không tồn tại, tiến hành insert bản ghi mới.

Và bây giờ có 2 account cùng giá trị email trong DB. Điều này sẽ là lỗ hổng không thể chấp nhận được! Và phải xử lý trường hợp này như nào?

Unique indexes are the solution

Giải pháp ở đây là chúng ta đẩy công việc này cho hệ quản trị cơ sở dữ liệu xử lý. Để làm điều này chúng ta cần thêm một thuộc tính tự unique index cho column thuộc table mà chúng ta cần giá trị của nó là duy nhất.

add_index :email, unique: true

Kết luận

Trong spec có yêu cầu validate uniqueness thì có một lời khuyên là nên implement cả 2 phương pháp để đảm bảo tính duy nhất. Có validation của Rails sẽ đảm bảo rằng bạn nhận được thông báo lỗi đẹp để hiển thị cho người dùng của bạn, nhưng cũng cần có validation ở cấp database để đảm bảo rằng dữ liệu của bạn không bị trùng lặp. Một lợi ích nữa của việc đánh index và unique là tăng tốc độ của câu truy vấn.

Hy vọng bài viết hỗ trợ các bạn kiểm tra validation unique trong quá trình code.

Nội dung bài viết được dịch từ đây


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí