Active Record 数据验证
數據驗證概覽
為什么要做數據驗證
數據驗證確保只有有效的數據才能存入數據庫,在模型中做驗證是最有保障的,只有通過驗證的數據才能存入數據庫。數據驗證和使用的數據庫種類無關,終端用戶也無法跳過,而且容易測試和維護。
數據驗證的方式主要有數據庫原生約束、客戶端驗證和控制器層驗證:
-
數據庫約束無法兼容多種數據庫,難以測試和維護,但是如果其他應用也要使用這個數據庫,最好能夠在數據庫層做一些約束。
-
客戶端驗證可靠性不高,但是和其他驗證方式結合可以提供實時反饋
-
控制器層驗證不靈便,難以測試和維護,只要可能就應該保證控制器的代碼簡潔,這樣才有利于長遠發展
Active Record 對象分為兩種,一種在數據庫中有對應記錄,一種沒有,新建對象還不屬于數據庫,只有調用了 save 方法后,才會存入數據庫,可以使用 new_record? 方法判斷是否存入數據庫,未存入則返回 true ,存入則返回 false
新建并保存會執行 SQL INSERT 操作,更新記錄會執行 SQL UPDATE 操作,一般情況下,數據驗證發生在執行這些SQL語句之前,如果驗證失敗,對象會被標記為無效, Active Record 不會向數據庫發送指令。
以下方法會觸發數據驗證:
-
create
-
create!
-
save
-
save!
-
update
-
update!
炸彈方法會在驗證失敗后拋出異常。
以下方法會跳過驗證,不管驗證是否通過都會把對象存入數據庫:
-
decrement!
-
decrement_counter
-
increment!
-
increment_counter
-
toggle!
-
touch
-
update_all
-
update_attribute
-
update_column
-
update_columns
-
update_counters
同時,使用 save 方法時,如果傳入 validate: false 參數,也會跳過驗證。
同時,也可以使用 valid? 方法自己執行驗證,如果對象上沒有錯誤則返回 true ,否則返回 false,invalid? 方法則相反。執行驗證之后,錯誤可以通過實例方法 errors.message 獲取,這個方法返回一個錯誤集合,如果為空,則說明對象是有效的。需要注意的是,如果沒有驗證數據,這個方法返回的也是一個空集合。
如果要驗證某個屬性是否有效,可以使用 errors[:attribute] ,這返回一個包含了所有錯誤的數組,如果沒有錯誤則返回空數組,這個方法和 invalid? 方法不一樣,這個方法不會驗證整個對象,只會檢查某個屬性是否有錯。
可以使用 errors.details[:attribute] 檢查到底是哪個驗證導致屬性無效,這個方法返回一個由散列組成的數組。
數據驗證的輔助方法
輔助方法可以直接在模型中使用,這些方法提供了常用的驗證規則,驗證失敗就會向對象的 errors 集合中添加一個消息。
每個輔助方法都可以接受任意個屬性名,所以一行代碼可以在多個屬性上做同一種驗證。
acceptance
檢查表單提交時,用戶界面中的復選框是否被選中,一般用來要求用戶接受應用的服務條款、確保用戶閱讀了一些文本等。
class Person < ApplicationRecordvalidates :terms_of_service, acceptance: true end 復制代碼validates_associated
如果模型與其他模型有關聯,而且關聯的模型也需要驗證,就是用這個方法,保存對象時,會在相關聯的每個對象上調用 valid? 方法。
class Library < ApplicationRecord has_many :booksvalidates_associated :books end 復制代碼不要在關聯的兩端使用,這樣會造成無限的循環
confirmation
檢查兩個文本字段的值是否完全相同,如確認郵件地址或者密碼。這個驗證創建一個虛擬屬性,其名字為要驗證的屬性名后加 _confirmation 。
class Person < ApplicationRecordvalidates :email, confirmation: true end 復制代碼在視圖模板中視圖可以如下:
<%= text_field :person, :email %> <%= text_field :person, :email_confirmation %> 復制代碼因為只有在 email_confirmation 值不是 nil 時才會驗證,所以需要添加存在性驗證
class Person < ApplicationRecordvalidates :email, confirmation: truevalidates :email_confirmation, presence: true end 復制代碼使用 :case_sensitive 選項可以說明是否區分大小寫,這個選項默認值是true
class Person < ApplicationRecordvalidates :email, confirmation: {case_sensitive: false} end 復制代碼exclusion
這個方法檢查屬性的值是否不在指定的集合中,集合可以是任何一種可枚舉的對象
class Account < Applicationvalidates :subdomain, exclusion: {in: %w(www us ca jp), message: "%{value} is reserved"} end 復制代碼in 選項設置哪些值不能作為屬性的值,in 的別名是 with
formate
這個方法檢查屬性的值是否匹配 :with 選項指定的正則表達式。
class Product < ApplicationReocrdvalidates :legacy_code, formate: {with: /\A[a-zA-Z]+\z/, message: "only allows letters"} end 復制代碼inclusion
這個方法檢查屬性的值是否在指定的集合中,集合可以是任何一種可枚舉的對象
class Coffee < ApplicationRecordvalidates :size, inclusion: {in: %w(small mediun large), message: "%{value} is not a valid size"} end 復制代碼length
這個方法驗證屬性值的長度,有多個選項
class Person < ApplicationRecordvalidates :name, length: {minimum: 2}validates :bio, length: {maximum: 500}validates :password, length: {in: 6..20}validates :registration_number, length:{is: 6} end 復制代碼可用的長度約束選項有:
-
:minimum:最短長度
-
:maximum:最長長度
-
:in 或者 :within:長度范圍
-
:is:等于該長度
定制錯誤消息可以使用 :wrong_length 、:too_long 、:too_short 選項,%{count} 表示長度限制的值
class Person < ApplicationRecordvalidates :bio, length: {maximum: 1000, too_long: "%{count} characters is the maximum allowed"} end 復制代碼numericality
檢查屬性是否只包含數字,默認匹配的值是可選的正負符號后加整數或浮點數,如果只接受整數,把 :only_integer 選項設置為 true,否則會使用Float把值轉換為數字。
class Player < ApplicationRecordvalidates :points, numericality: truevalidates :games_played< numericality: {only_integer: true} end 復制代碼除此之外,這個方法還可指定以下選項:
-
:greater_than :屬性值需大于 >
-
:greater_than_or_equal_to :>=
-
:equal_to :=
-
:less_than :<
-
:less_than_or_equal_to :<=
-
:other_than :!=
-
:odd :必須為奇數
-
:even :必須為偶數
此方法默認不接受 nil 值,可以使用 allow_nil: true 選項允許接受 nil
presence
檢查屬性是否為非空值,方法調用 blank? 方法檢查是否為 nil 或空字符串
class Person < ApplicationRecordvalidates :name, :login, :email, presence: true end 復制代碼absence
驗證屬性值是否為空,使用 present? 方法檢查是否為 nil 或空字符串
class Person < ApplicationRecordvalidates :name, :login, :email, absence: true end 復制代碼uniqueness
這個方法在保存對象前驗證屬性值是否唯一,這個方法不會在數據庫中創建唯一性約束,所以有可能兩次數據庫連接創建的記錄具有相同的值,所以最好在數據庫字段上建立唯一性約束。
class Account < ApplicationRecordvalidates :email, uniqueness: true end 復制代碼這個驗證會在模型對應的表中執行一個 SQL 查詢,檢查現有的記錄中該字段是否已經出現過相同的值。
可以使用 :case_sensitive 選項
class Person < ApplicationRecordvalidates :name, uniqueness: {case_sensitive: false} end 復制代碼validates_with
這個方法把記錄交給其他類做驗證。
class GoodnessValidator < ActiveModel::Validatordef validate(record)if record.first_name == "Evil"record.errors[:base] << "This person is evil"endend endclass Person < ApplicationRecordvalidates_with GoodnessValidator end 復制代碼這個方法的參數是一個類或者一組類。
validates_each
這個方法使用代碼塊中的代碼驗證屬性,需要在代碼塊中定義驗證方式。
class Person < ApplicationRecordvalidates_each :name, :surname do |record, attr, value|record.errors.add(attr, 'must start with upper case') if value =~/\A[[:lower:]]/end end 復制代碼代碼塊的參數是記錄、屬性名和屬性值。
常用驗證選項
:allow_nil
允許 nil 值,如果要驗證的值是 nil 就跳過驗證
class Coffee < ApplicationRecordvalidates :size, inclusion: {in: %w(small medium large), message: "%{value} is not a valid size"}, allow_nil: true end 復制代碼:allow_blank
與上面方法類似,使用 blank? 方法判斷,空字符串和nil時跳過驗證
:message
添加錯誤消息,消息中可以包含 %{value} 、 %{attribute}、%{model}
:on
指定驗證時機,默認都在保存時驗證,使用使用
-
on: :create :只在創建時驗證
-
on: :update:只在更新時驗證
:strict
使用嚴格驗證模式,對象無效時拋出異常
class Person < ApplicationRecordvalidates :name, presence: { strict: true } endPerson.new.valid? # => ActiveModel::StrictValidationFailed: Name can't be blank復制代碼條件驗證
使用 :if 和 :unless 選項只有滿足特定條件才驗證,值可以是符號、字符串、Proc或數組。 選項為符號時,表示驗證之前執行對應的方法。這是最常用的設置方法。
class Order < ApplicationRecordvalidates :card_number end 復制代碼自定義驗證
自定義驗證類繼承自 ActiveModel::Validator,必須實現validate方法,參數是要驗證的記錄
class MyValidator < ActiveModel::Validatordef validate(record)unless record.name.starts_with? 'X'record.errors[:name] << 'Need a name starting with X please!'endend endclass Personinclude ActiveModel::Validationsvalidates_with MyValidator end 復制代碼驗證錯誤處理
ActiveModel::Errors 的實例包含所有的錯誤,鍵是每個屬性的名稱,只是一個數組,包含錯誤消息字符串。
errors[] 用于獲取某個屬性上的錯誤消息
errors.add 用于手動添加某屬性的錯誤消息,參數是屬性和錯誤消息
errors.details 返回錯誤詳情
errors.clear 清楚errors集合中的所有消息
errors.size 返回錯誤消息總數。
總結
以上是生活随笔為你收集整理的Active Record 数据验证的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用政府开放数据和低代码方案构建应用
- 下一篇: 使用srvany.exe将任何程序作为W