跳至主要内容

[Rails] Active Record Validations(Model 資料驗證)

keywords: 資料驗證, valid, error

資料驗證這件事在 Rails 的 MVC 三分天下的架構中,Controller 跟 Model 都可以做這件事,要在 View 裡寫 JavaScript 做檢查也可以,但這件事如果交給 Controller 或 View 來做的話,一來會讓程式碼的邏輯變得更複雜,二來這個驗證也不容易被重複使用,也不容易被測試,所以資料機制寫在 Model 裡是比較合理而且單純的。

Active Record Validations @ RailsGuides

Model 資料驗證設定

除了 presence 之外,Rails 還有提供其它像是 uniqueness、length 或 numericality 等便利的驗證器,使用方法可直接參考 Rails Guide 的 Active Record Validations 章節

範例一

#####################################################
## 在 Model 中進行資料驗證 ##
#####################################################

class Candidate < ApplicationRecord
validates :name, presence: true # 寫法一
validates_presence_of :party # 寫法二
end

範例二

validates:是標準 Rails 的驗證器(validator) presence: true:則是告訴驗證器要驗證每個欄位是否存在,而且不是空值。 numericality: 用來確認內容是有效的數值 uniqueness:檢驗資料庫中有無具有相同 title 的列 allow_blank:用來允許欄位是空值

class Product < ApplicationRecord
validates :title, :description, :image_url, presence: true
validates :price, numericality: {greater_than_or_equal_to: 0.01}
validates :title, uniqueness: true
validates :image_url, allow_blank: true, format:{
with: %r{\.(gif|jpg|png)\Z}i,
message: 'must be a URL for GIF, JPG or PNG image.'
}
end

檢視錯誤訊息

# 在 Rails Console 中測試

c1 = Candidate.new
c1.errors.any? # 回傳 false,檢驗 c1 有無錯誤
c1.valid? # 驗證資料格式是否正確(透過 save 也會進行驗證)
c1.errors.any? # 回傳 true
c1.errors.full_messages # 回傳 "Name can't be blank",顯示錯誤訊息內容

c1.save(validates: false) # 跳過資料驗證

注意事項

雖然驗證功能很方便,但並不是每種方法都會觸發驗證,僅有以下這些方法會觸發驗證 create, create!, save, save!, update, update!,而 toggle!increment! 等方法會跳過驗證流程。

有驚嘆號版本的,如果驗證未通過會產生錯誤訊息,而沒有驚嘆號版本則僅會回傳該 Model 的一個空物件。

客制化資料驗證

方法一:遵循 Rails 驗證器規則

這個驗證器可以跟其它內建的驗證器一起混著使用,使用起來會更簡潔。要寫這樣的驗證器需要符合 Rails Validator 的命名規則:

  1. 參數是 begin_with_ruby 的話,類別名稱則是 BeginWithRuby 加上 Validator,並繼承自 ActiveModel::EachValidator 類別。
  2. 必須實作 validate_each 方法。
# 客制化驗證器(class)要放在驗證步驟(validates)前

class BeginWithRubyValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
unless value.starts_with? 'Ruby'
record.errors[attribute] << "必需是 Ruby 開頭喔!"
end
end
end

validates :name, begin_with_ruby: true

方法二:使用 validate 自己寫判斷

class Candidate < ApplicationRecord
validate :name_validator # 這裡是 validate(單數)不是上面的 validates

private
def name_validator
unless name.starts_with? 'Ruby'
errors[:name] << '必需是 Ruby 開頭喔!' # 當不符合規定時,就在 errors 的 Hash 裡面塞錯誤訊息。
end
end

end

關閉錯誤區塊(field with errors)

keywords: field_with_errors

當表單驗證出現錯誤時,會自動為該 input 帶上一個 .field_with_errors<div> 區塊。如果不希望出現此區塊,可以在 config/environment.rb 中加上:

# ./OnePageShop/config/environment.rb
ActionView::Base.field_error_proc = Proc.new do |html_tag, instance|
html_tag.html_safe
end

Remove Rails field_with_errors wrapper

範例

# ./OnePageShop/app/models/invoice.rb

class Invoice < ApplicationRecord
validates :address_line, presence: true
validates :ban, :company, presence: true, if: :is_three_copies?

private
def is_three_copies?
inv_type == 'three_copies'
end
end

參考