Tìm hiểu về Active Record Validations

Tram Ho

1. Validations là gì?

1.1 Tại sao dùng validations?

Validations là cách để đảm bảo dữ liệu của bạn hợp lệ trước khi lưu vào database.
Có nhiều cách để thực hiện validate:

  • DB constraints: Khiến cho cơ sở dữ liệu trở lên độc lập và khó để test hoặc bảo trì.
    Ví dụ trong Rails tuts có đoạn này:

  • Client-side validations: Dễ bị bỏ qua. Ví dụ nếu sử dụng JS để validations, nếu trên browser mà JS bị tắt, thì validations sẽ bị bỏ qua. Tuy nhiên nếu kết hợp được với các kỹ thuật khác, đây sẽ là 1 cách rất hay để validations và trả lại feedback ngay lập tức cho user.
  • Controller-level validations: Rất khó để test validations ở level này. Ngoài ra, nó sẽ làm controller bị béo (nhiều code). Controller nên gầy 1 tý để ứng dụng có thể chạy trong thời gian dài.

Chốt lại, team Rails bảo là model-level validations là dễ dùng nhất.

1.2 When Does Validation Happen?

Có 2 loại ActiveRecord object: loại liên quan và loại không liên quan đến một record trong cơ sở dữ liệu.

Loại không liên quan được tạo bằng method new. Ta sử dụng method new_record? để phân biệt 2 loại object này.

Validations ở model level luôn chỉ chạy khi quá trình create hoặc update 1 bản ghi thông qua ActiveRecord được diễn ra.
Khi khởi tạo bản ghi thì ActiveRecord sẽ gửi 1 câu INSERT còn khi cập nhật thì nó gửi 1 câuUPDATE query vào cơ sở dữ liệu.
Validations luôn chạy trước khi ActiveRecord định thực hiện câu INSERT hoặc UPDATE vào cơ sở dữ liệu.

Các method dưới đây sẽ gọi vào validations:

1.3 Skipping validations

Các method sau sẽ skip validations:

Hoặc có thể dùng save(validate: false) để skip validations

1.4 valid? , invalid?

Trước khi 1 ActiveRecord object được save, Rails sẽ chạy validations. Nếu validations có lỗi, Rails sẽ không save object lại. Các lỗi của object được lưu bởi collection .errors.messages.

  • Sau khi chạy validations, nếu a.errors.messages rỗng thì a là 1 valid object .

  • Các object tạo từ method .new không bao giờ trả về errors, vì nó không chạy validations.

  • valid? method sẽ triggers validations và trả về giá trị false nếu errors.messages rỗng.

invalid? chỉ đơn giản là method đảo của valid? . Nó cũng triggers validations với object và trả về giá trị boolean ngược lại với valid?

1.5 errors

Để xác định xem 1 attribute nhất định của object có valid không, ta làm kiểm tra bằng cú pháp object.errors[:attributes].any? :

Cú pháp này chỉ sử dụng được sau khi quá trình validations được diễn ra.

1.6 errors.details

Lấy ra symbol của validator:

2. Validation helper

  • ActiveRecord cung cấp nhiều validation helpers. Các helper này xây dựng dựa trên nhiều nguyên tắc validations phổ biến.
  • Nếu validation fail, 1 message sẽ được add vào object.errors và messages này liên kết với attribute đã được validate.
  • Mỗi validation helper có thể truyền vào nhiều attributes.

  • Mỗi validation helper đều có 1 default message. Option :message sẽ có ích khi bạn không muốn dùng default message :

Bây giờ, chúng ta cùng tìm hiểu 1 số validations helper phổ biến.

2.1 acceptance

Ta thường sử dụng acceptance kết hợp với thẻ <input type="checkbox"> . Giả sử ta có 1 cái form yêu cầu nhập fields is_teen như sau:

Ta thêm helper acceptance vào model User

Khi checkbox không được tích vào thì user_params sẽ có giá trị thế này:

Và quá trình validations diễn ra như thế này:

2.2 inclusion, exclusion

Helper inclusion dùng để kiểm tra attribute nhập vào có nằm trong 1 tập hợp các giá trị cho trước không.

Cùng xem ví dụ dưới đây để hiểu:

Ngược lại với inclusion, chúng ta helper exclusion . Nếu attribute nhập vào không nằm trong tập các giá trị cho trước thì được coi là valid .

2.2 presence, absence

Helper presence dùng để kiểm tra xem attribute có phải 1 blank hay không, thông qua method blank?. Nếu attribute.blank? trả về true thì sau khi validations sẽ thực hiện rollback.

Đầu tiên, bạn cần biết khi nào thì method blank? trả về true.

Thử với ví dụ dưới đây:

presence nghĩa là sự có mặt, nên thường chúng ta sẽ nghĩ helper này dùng để kiểm tra sự có mặt của attribute .Tuy nhiên với boolean value thì không hẳn thế .

false.blank? cũng trả về true nên để validates sự có mặt attribute theo kiểu boolean, chúng ta nên dùng inclusion:

Ngược lại với helper presence, helper absence dùng method present? kiểm tra xem attribute truyền vào có rỗng hay không. Nếu attribute.present? trả về true thì sau validation sẽ thực hiện rollback.

2.4 numericality

Helper này dùng để đảm bảo các thuộc tính phải là kiểu Numeric . Nếu attribute không phải là kiểu Numeric, sau khi validations sẽ thực hiện rollback. Default message của helper này là [is not a number]

Có khá nhiều tùy chọn với helper này. Nếu bạn muốn attribute được validate chỉ thuộc kiểu integer, chúng ta có option only_integer: true

Ngoài ra chúng ta còn có một loạt các tùy chọn khác tương ứng với các symbol sau:

  • :greater_than : đảm bảo attribute phải lớn hơn 1 giá trị mà bạn muốn.
  • :greater_than_or_equal_to : đảm bảo attribute phải lớn hơn hoặc bằng 1 giá trị mà bạn muốn.
  • :equal_to : đảm bảo attribute phải bằng một giá trị mà bạn muốn.
  • :odd : đảm bảo attribute phải là một số lẻ.
  • :even : đảm bảo attribute phải là một số chẵn.

2.5 uniqueness

uniqueness hoạt động như nào?

thêm option case_sensitive.

Với bài toán, 1 người không được follow người khác 2 lần.

Còn rất nhiều validation helper khác rất hữu dụng, các bạn có thể tìm đọc thêm tại đây.

3. Một số option phổ biến trong các helper

3.1 allow_nil

Bạn muốn :name không thể là rỗng, nhưng chấp nhận giá trị nil.

3.2 allow_blank

3.3 :on

4. Custom validation

Nếu những validations helper mà ActiveRecord tạo sẵn cho bạn vẫn không thể giải quyết được bài toán mà bạn đang gặp phải, thì bạn có thể tự validation theo cách của mình .
Mình tìm thấy 2 cách có thể thực hiện custom validation: custom methodcustom validator

4.1 Custom method

Ví dụ, mình có bài toán follow rất quen thuộc với 2 bảng UserRelationship như sau.

Bây giờ mình muốn validate trường hợp, user không thể tự follow chính mình. Nghĩa là khi 1 bản ghi Relationship được tạo, nếu follower_id == followed_id trả về true, thì sau quá trình validation phải thực hiện rollback .

Bài toán này khá khó để dùng các validation helper sẵn có của ActiveRecord, vì vậy mình sẽ thực hiện custom validation như sau .

Thực hiện validate:

Phương pháp thực hiện validate nói trên được gọi là custom validation sử dụng custom method.

3.2 Custom validator.

Giả sử trong rails app của bạn có các model Course, PlanEvent . Cả 3 bảng này đều có 2 trường start_dateend_date . Bạn cần validates ở cả 3 model, để đảm bảo rằng start_date luôn phải nhỏ hơn hoặc bằng end_date . Sẽ có 2 vấn đề ở bài toán này:

  • Một là, khó để tìm ra 1 validation helper có sẵn mà phù hợp với yêu cầu của bài toán, vậy nên chúng ta sẽ sử dụng custom validation.
  • Hai là, nếu sử dụng custom methodchúng ta sẽ phải viết lại method đấy 3 lần ở mỗi model. Như thế khá khó để sử dụng lại code .

Để giải quyết cả 2 vấn đề nói trên, mình sẽ viết 1 class riêng chứa validator, và gọi lại class này ở mỗi model cần validate.

Đầu tiên mình tạo một thư lục để lưu các class validators và config để rails có thể load được nó trong file application.rb

Giờ mình sẽ gọi validator này với method validates_with

Thử chạy validate nhé:

Đó là tất cả những gì mình muốn trình bày trong bài viết lần này.


References:

https://guides.rubyonrails.org/active_record_validations.html

Chia sẻ bài viết ngay

Nguồn bài viết : Viblo