<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                # Active Record 數據驗證 本文介紹如何使用 Active Record 提供的數據驗證功能在數據存入數據庫之前驗證對象的狀態。 讀完本文,你將學到: * 如何使用 Active Record 內建的數據驗證幫助方法; * 如何編寫自定義的數據驗證方法; * 如何處理驗證時產生的錯誤消息; ### Chapters 1. [數據驗證簡介](#%E6%95%B0%E6%8D%AE%E9%AA%8C%E8%AF%81%E7%AE%80%E4%BB%8B) * [為什么要做數據驗證?](#%E4%B8%BA%E4%BB%80%E4%B9%88%E8%A6%81%E5%81%9A%E6%95%B0%E6%8D%AE%E9%AA%8C%E8%AF%81%EF%BC%9F) * [什么時候做數據驗證?](#%E4%BB%80%E4%B9%88%E6%97%B6%E5%80%99%E5%81%9A%E6%95%B0%E6%8D%AE%E9%AA%8C%E8%AF%81%EF%BC%9F) * [跳過驗證](#%E8%B7%B3%E8%BF%87%E9%AA%8C%E8%AF%81) * [`valid?` 和 `invalid?`](#valid-questionmark-%E5%92%8C-invalid-questionmark) * [`errors[]`](#%E6%95%B0%E6%8D%AE%E9%AA%8C%E8%AF%81%E7%AE%80%E4%BB%8B-errors%5B%5D) 2. [數據驗證幫助方法](#%E6%95%B0%E6%8D%AE%E9%AA%8C%E8%AF%81%E5%B8%AE%E5%8A%A9%E6%96%B9%E6%B3%95) * [`acceptance`](#acceptance) * [`validates_associated`](#validates_associated) * [`confirmation`](#confirmation) * [`exclusion`](#exclusion) * [`format`](#format) * [`inclusion`](#inclusion) * [`length`](#length) * [`numericality`](#numericality) * [`presence`](#presence) * [`absence`](#absence) * [`uniqueness`](#uniqueness) * [`validates_with`](#validates_with) * [`validates_each`](#validates_each) 3. [常用的驗證選項](#%E5%B8%B8%E7%94%A8%E7%9A%84%E9%AA%8C%E8%AF%81%E9%80%89%E9%A1%B9) * [`:allow_nil`](#:allow_nil) * [`:allow_blank`](#:allow_blank) * [`:message`](#:message) * [`:on`](#:on) 4. [嚴格驗證](#%E4%B8%A5%E6%A0%BC%E9%AA%8C%E8%AF%81) 5. [條件驗證](#%E6%9D%A1%E4%BB%B6%E9%AA%8C%E8%AF%81) * [指定 Symbol](#%E6%8C%87%E5%AE%9A-symbol) * [指定字符串](#%E6%8C%87%E5%AE%9A%E5%AD%97%E7%AC%A6%E4%B8%B2) * [指定 Proc](#%E6%8C%87%E5%AE%9A-proc) * [條件組合](#%E6%9D%A1%E4%BB%B6%E7%BB%84%E5%90%88) * [聯合條件](#%E8%81%94%E5%90%88%E6%9D%A1%E4%BB%B6) 6. [自定義驗證方式](#%E8%87%AA%E5%AE%9A%E4%B9%89%E9%AA%8C%E8%AF%81%E6%96%B9%E5%BC%8F) * [自定義驗證使用的類](#%E8%87%AA%E5%AE%9A%E4%B9%89%E9%AA%8C%E8%AF%81%E4%BD%BF%E7%94%A8%E7%9A%84%E7%B1%BB) * [自定義驗證使用的方法](#%E8%87%AA%E5%AE%9A%E4%B9%89%E9%AA%8C%E8%AF%81%E4%BD%BF%E7%94%A8%E7%9A%84%E6%96%B9%E6%B3%95) 7. [處理驗證錯誤](#%E5%A4%84%E7%90%86%E9%AA%8C%E8%AF%81%E9%94%99%E8%AF%AF) * [`errors`](#errors) * [`errors[]`](#%E5%A4%84%E7%90%86%E9%AA%8C%E8%AF%81%E9%94%99%E8%AF%AF-errors%5B%5D) * [`errors.add`](#errors.add) * [`errors[:base]`](#errors%5B:base%5D) * [`errors.clear`](#errors.clear) * [`errors.size`](#errors.size) 8. [在視圖中顯示驗證錯誤](#%E5%9C%A8%E8%A7%86%E5%9B%BE%E4%B8%AD%E6%98%BE%E7%A4%BA%E9%AA%8C%E8%AF%81%E9%94%99%E8%AF%AF) ### 1 數據驗證簡介 下面演示一個非常簡單的數據驗證: ``` class Person < ActiveRecord::Base validates :name, presence: true end Person.create(name: "John Doe").valid? # => true Person.create(name: nil).valid? # => false ``` 如上所示,如果 `Person`的 `name` 屬性值為空,驗證就會將其視為不合法對象。創建的第二個 `Person` 對象不會存入數據庫。 在深入探討之前,我們先來介紹數據驗證在整個程序中的作用。 #### 1.1 為什么要做數據驗證? 數據驗證能確保只有合法的數據才會存入數據庫。例如,程序可能需要用戶提供一個合法的 Email 地址和郵寄地址。在模型中做驗證是最有保障的,只有通過驗證的數據才能存入數據庫。數據驗證和使用的數據庫種類無關,終端用戶也無法跳過,而且容易測試和維護。在 Rails 中做數據驗證很簡單,Rails 內置了很多幫助方法,能滿足常規的需求,而且還可以編寫自定義的驗證方法。 數據存入數據庫之前的驗證方法還有其他幾種,包括數據庫內建的約束,客戶端驗證和控制器層驗證。下面列出了這幾種驗證方法的優缺點: * 數據庫約束和“存儲過程”無法兼容多種數據庫,而且測試和維護較為困難。不過,如果其他程序也要使用這個數據庫,最好在數據庫層做些約束。數據庫層的某些驗證(例如在使用量很高的數據表中做唯一性驗證)通過其他方式實現起來有點困難。 * 客戶端驗證很有用,但單獨使用時可靠性不高。如果使用 JavaScript 實現,用戶在瀏覽器中禁用 JavaScript 后很容易跳過驗證。客戶端驗證和其他驗證方式結合使用,可以為用戶提供實時反饋。 * 控制器層驗證很誘人,但一般都不靈便,難以測試和維護。只要可能,就要保證控制器的代碼簡潔性,這樣才有利于長遠發展。 你可以根據實際的需求選擇使用哪種驗證方式。Rails 團隊認為,模型層數據驗證最具普適性。 #### 1.2 什么時候做數據驗證? 在 Active Record 中對象有兩種狀態:一種在數據庫中有對應的記錄,一種沒有。新建的對象(例如,使用 `new` 方法)還不屬于數據庫。在對象上調用 `save` 方法后,才會把對象存入相應的數據表。Active Record 使用實例方法 `new_record?` 判斷對象是否已經存入數據庫。假如有下面這個簡單的 Active Record 類: ``` class Person < ActiveRecord::Base end ``` 我們可以在 `rails console` 中看一下到底怎么回事: ``` $ rails console >> p = Person.new(name: "John Doe") => #<Person id: nil, name: "John Doe", created_at: nil, updated_at: nil> >> p.new_record? => true >> p.save => true >> p.new_record? => false ``` 新建并保存記錄會在數據庫中執行 SQL `INSERT` 操作。更新現有的記錄會在數據庫上執行 SQL `UPDATE` 操作。一般情況下,數據驗證發生在這些 SQL 操作執行之前。如果驗證失敗,對象會被標記為不合法,Active Record 不會向數據庫發送 `INSERT` 或 `UPDATE` 指令。這樣就可以避免把不合法的數據存入數據庫。你可以選擇在對象創建、保存或更新時執行哪些數據驗證。 修改數據庫中對象的狀態有很多方法。有些方法會做數據驗證,有些則不會。所以,如果不小心處理,還是有可能把不合法的數據存入數據庫。 下列方法會做數據驗證,如果驗證失敗就不會把對象存入數據庫: * `create` * `create!` * `save` * `save!` * `update` * `update!` 爆炸方法(例如 `save!`)會在驗證失敗后拋出異常。驗證失敗后,非爆炸方法不會拋出異常,`save` 和 `update` 返回 `false`,`create` 返回對象本身。 #### 1.3 跳過驗證 下列方法會跳過驗證,不管驗證是否通過都會把對象存入數據庫,使用時要特別留意。 * `decrement!` * `decrement_counter` * `increment!` * `increment_counter` * `toggle!` * `touch` * `update_all` * `update_attribute` * `update_column` * `update_columns` * `update_counters` 注意,使用 `save` 時如果傳入 `validate: false`,也會跳過驗證。使用時要特別留意。 * `save(validate: false)` #### 1.4 `valid?` 和 `invalid?` Rails 使用 `valid?` 方法檢查對象是否合法。`valid?` 方法會觸發數據驗證,如果對象上沒有錯誤,就返回 `true`,否則返回 `false`。前面我們已經用過了: ``` class Person < ActiveRecord::Base validates :name, presence: true end Person.create(name: "John Doe").valid? # => true Person.create(name: nil).valid? # => false ``` Active Record 驗證結束后,所有發現的錯誤都可以通過實例方法 `errors.messages` 獲取,該方法返回一個錯誤集合。如果數據驗證后,這個集合為空,則說明對象是合法的。 注意,使用 `new` 方法初始化對象時,即使不合法也不會報錯,因為這時還沒做數據驗證。 ``` class Person < ActiveRecord::Base validates :name, presence: true end >> p = Person.new # => #<Person id: nil, name: nil> >> p.errors.messages # => {} >> p.valid? # => false >> p.errors.messages # => {name:["can't be blank"]} >> p = Person.create # => #<Person id: nil, name: nil> >> p.errors.messages # => {name:["can't be blank"]} >> p.save # => false >> p.save! # => ActiveRecord::RecordInvalid: Validation failed: Name can't be blank >> Person.create! # => ActiveRecord::RecordInvalid: Validation failed: Name can't be blank ``` `invalid?` 是 `valid?` 的逆測試,會觸發數據驗證,如果找到錯誤就返回 `true`,否則返回 `false`。 #### 1.5 `errors[]` 要檢查對象的某個屬性是否合法,可以使用 `errors[:attribute]`。`errors[:attribute]` 中包含 `:attribute` 的所有錯誤。如果某個屬性沒有錯誤,就會返回空數組。 這個方法只在數據驗證之后才能使用,因為它只是用來收集錯誤信息的,并不會觸發驗證。而且,和前面介紹的 `ActiveRecord::Base#invalid?` 方法不一樣,因為 `errors[:attribute]` 不會驗證整個對象,只檢查對象的某個屬性是否出錯。 ``` class Person < ActiveRecord::Base validates :name, presence: true end >> Person.new.errors[:name].any? # => false >> Person.create.errors[:name].any? # => true ``` 我們會在“[處理驗證錯誤](#working-with-validation-errors)”一節詳細介紹驗證錯誤。現在,我們來看一下 Rails 默認提供的數據驗證幫助方法。 ### 2 數據驗證幫助方法 Active Record 預先定義了很多數據驗證幫助方法,可以直接在模型類定義中使用。這些幫助方法提供了常用的驗證規則。每次驗證失敗后,都會向對象的 `errors` 集合中添加一個消息,這些消息和所驗證的屬性是關聯的。 每個幫助方法都可以接受任意數量的屬性名,所以一行代碼就能在多個屬性上做同一種驗證。 所有的幫助方法都可指定 `:on` 和 `:message` 選項,指定何時做驗證,以及驗證失敗后向 `errors` 集合添加什么消息。`:on` 選項的可選值是 `:create` 和 `:update`。每個幫助函數都有默認的錯誤消息,如果沒有通過 `:message` 選項指定,則使用默認值。下面分別介紹各幫助方法。 #### 2.1 `acceptance` 這個方法檢查表單提交時,用戶界面中的復選框是否被選中。這個功能一般用來要求用戶接受程序的服務條款,閱讀一些文字,等等。這種驗證只針對網頁程序,不會存入數據庫(如果沒有對應的字段,該方法會創建一個虛擬屬性)。 ``` class Person < ActiveRecord::Base validates :terms_of_service, acceptance: true end ``` 這個幫助方法的默認錯誤消息是“must be accepted”。 這個方法可以指定 `:accept` 選項,決定可接受什么值。默認為“1”,很容易修改: ``` class Person < ActiveRecord::Base validates :terms_of_service, acceptance: { accept: 'yes' } end ``` #### 2.2 `validates_associated` 如果模型和其他模型有關聯,也要驗證關聯的模型對象,可以使用這個方法。保存對象時,會在相關聯的每個對象上調用 `valid?` 方法。 ``` class Library < ActiveRecord::Base has_many :books validates_associated :books end ``` 這個幫助方法可用于所有關聯類型。 不要在關聯的兩端都使用 `validates_associated`,這樣會生成一個循環。 `validates_associated` 的默認錯誤消息是“is invalid”。注意,相關聯的每個對象都有各自的 `errors` 集合,錯誤消息不會都集中在調用該方法的模型對象上。 #### 2.3 `confirmation` 如果要檢查兩個文本字段的值是否完全相同,可以使用這個幫助方法。例如,確認 Email 地址或密碼。這個幫助方法會創建一個虛擬屬性,其名字為要驗證的屬性名后加 `_confirmation`。 ``` class Person < ActiveRecord::Base validates :email, confirmation: true end ``` 在視圖中可以這么寫: ``` <%= text_field :person, :email %> <%= text_field :person, :email_confirmation %> ``` 只有 `email_confirmation` 的值不是 `nil` 時才會做這個驗證。所以要為確認屬性加上存在性驗證(后文會介紹 `presence` 驗證)。 ``` class Person < ActiveRecord::Base validates :email, confirmation: true validates :email_confirmation, presence: true end ``` 這個幫助方法的默認錯誤消息是“doesn't match confirmation”。 #### 2.4 `exclusion` 這個幫助方法檢查屬性的值是否不在指定的集合中。集合可以是任何一種可枚舉的對象。 ``` class Account < ActiveRecord::Base validates :subdomain, exclusion: { in: %w(www us ca jp), message: "%{value} is reserved." } end ``` `exclusion` 方法要指定 `:in` 選項,設置哪些值不能作為屬性的值。`:in` 選項有個別名 `:with`,作用相同。上面的例子設置了 `:message` 選項,演示如何獲取屬性的值。 默認的錯誤消息是“is reserved”。 #### 2.5 `format` 這個幫助方法檢查屬性的值是否匹配 `:with` 選項指定的正則表達式。 ``` class Product < ActiveRecord::Base validates :legacy_code, format: { with: /\A[a-zA-Z]+\z/, message: "only allows letters" } end ``` 默認的錯誤消息是“is invalid”。 #### 2.6 `inclusion` 這個幫助方法檢查屬性的值是否在指定的集合中。集合可以是任何一種可枚舉的對象。 ``` class Coffee < ActiveRecord::Base validates :size, inclusion: { in: %w(small medium large), message: "%{value} is not a valid size" } end ``` `inclusion` 方法要指定 `:in` 選項,設置可接受哪些值。`:in` 選項有個別名 `:within`,作用相同。上面的例子設置了 `:message` 選項,演示如何獲取屬性的值。 該方法的默認錯誤消息是“is not included in the list”。 #### 2.7 `length` 這個幫助方法驗證屬性值的長度,有多個選項,可以使用不同的方法指定長度限制: ``` class Person < ActiveRecord::Base validates :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`:屬性值的長度必須等于指定值; 默認的錯誤消息根據長度驗證類型而有所不同,還是可以 `:message` 定制。定制消息時,可以使用 `:wrong_length`、`:too_long` 和 `:too_short` 選項,`%{count}` 表示長度限制的值。 ``` class Person < ActiveRecord::Base validates :bio, length: { maximum: 1000, too_long: "%{count} characters is the maximum allowed" } end ``` 這個幫助方法默認統計字符數,但可以使用 `:tokenizer` 選項設置其他的統計方式: ``` class Essay < ActiveRecord::Base validates :content, length: { minimum: 300, maximum: 400, tokenizer: lambda { |str| str.scan(/\w+/) }, too_short: "must have at least %{count} words", too_long: "must have at most %{count} words" } end ``` 注意,默認的錯誤消息使用復數形式(例如,“is too short (minimum is %{count} characters”),所以如果長度限制是 `minimum: 1`,就要提供一個定制的消息,或者使用 `presence: true` 代替。`:in` 或 `:within` 的值比 1 小時,都要提供一個定制的消息,或者在 `length` 之前,調用 `presence` 方法。 #### 2.8 `numericality` 這個幫助方法檢查屬性的值是否值包含數字。默認情況下,匹配的值是可選的正負符號后加整數或浮點數。如果只接受整數,可以把 `:only_integer` 選項設為 `true`。 如果 `:only_integer` 為 `true`,則使用下面的正則表達式驗證屬性的值。 ``` /\A[+-]?\d+\Z/ ``` 否則,會嘗試使用 `Float` 把值轉換成數字。 注意上面的正則表達式允許最后出現換行符。 ``` class Player < ActiveRecord::Base validates :points, numericality: true validates :games_played, numericality: { only_integer: true } end ``` 除了 `:only_integer` 之外,這個方法還可指定以下選項,限制可接受的值: * `:greater_than`:屬性值必須比指定的值大。該選項默認的錯誤消息是“must be greater than %{count}”; * `:greater_than_or_equal_to`:屬性值必須大于或等于指定的值。該選項默認的錯誤消息是“must be greater than or equal to %{count}”; * `:equal_to`:屬性值必須等于指定的值。該選項默認的錯誤消息是“must be equal to %{count}”; * `:less_than`:屬性值必須比指定的值小。該選項默認的錯誤消息是“must be less than %{count}”; * `:less_than_or_equal_to`:屬性值必須小于或等于指定的值。該選項默認的錯誤消息是“must be less than or equal to %{count}”; * `:odd`:如果設為 `true`,屬性值必須是奇數。該選項默認的錯誤消息是“must be odd”; * `:even`:如果設為 `true`,屬性值必須是偶數。該選項默認的錯誤消息是“must be even”; 默認的錯誤消息是“is not a number”。 #### 2.9 `presence` 這個幫助方法檢查指定的屬性是否為非空值,調用 `blank?` 方法檢查值是否為 `nil` 或空字符串,即空字符串或只包含空白的字符串。 ``` class Person < ActiveRecord::Base validates :name, :login, :email, presence: true end ``` 如果要確保關聯對象存在,需要測試關聯的對象本身是否存在,而不是用來映射關聯的外鍵。 ``` class LineItem < ActiveRecord::Base belongs_to :order validates :order, presence: true end ``` 為了能驗證關聯的對象是否存在,要在關聯中指定 `:inverse_of` 選項。 ``` class Order < ActiveRecord::Base has_many :line_items, inverse_of: :order end ``` 如果驗證 `has_one` 或 `has_many` 關聯的對象是否存在,會在關聯的對象上調用 `blank?` 和 `marked_for_destruction?` 方法。 因為 `false.blank?` 的返回值是 `true`,所以如果要驗證布爾值字段是否存在要使用 `validates :field_name, inclusion: { in: [true, false] }`。 默認的錯誤消息是“can't be blank”。 #### 2.10 `absence` 這個方法驗證指定的屬性值是否為空,使用 `present?` 方法檢測值是否為 `nil` 或空字符串,即空字符串或只包含空白的字符串。 ``` class Person < ActiveRecord::Base validates :name, :login, :email, absence: true end ``` 如果要確保關聯對象為空,需要測試關聯的對象本身是否為空,而不是用來映射關聯的外鍵。 ``` class LineItem < ActiveRecord::Base belongs_to :order validates :order, absence: true end ``` 為了能驗證關聯的對象是否為空,要在關聯中指定 `:inverse_of` 選項。 ``` class Order < ActiveRecord::Base has_many :line_items, inverse_of: :order end ``` 如果驗證 `has_one` 或 `has_many` 關聯的對象是否為空,會在關聯的對象上調用 `present?` 和 `marked_for_destruction?` 方法。 因為 `false.present?` 的返回值是 `false`,所以如果要驗證布爾值字段是否為空要使用 `validates :field_name, exclusion: { in: [true, false] }`。 默認的錯誤消息是“must be blank”。 #### 2.11 `uniqueness` 這個幫助方法會在保存對象之前驗證屬性值是否是唯一的。該方法不會在數據庫中創建唯一性約束,所以有可能兩個數據庫連接創建的記錄字段的值是相同的。為了避免出現這種問題,要在數據庫的字段上建立唯一性索引。關于多字段索引的詳細介紹,參閱 [MySQL 手冊](http://dev.mysql.com/doc/refman/5.6/en/multiple-column-indexes.html)。 ``` class Account < ActiveRecord::Base validates :email, uniqueness: true end ``` 這個驗證會在模型對應的數據表中執行一個 SQL 查詢,檢查現有的記錄中該字段是否已經出現過相同的值。 `:scope` 選項可以指定其他屬性,用來約束唯一性驗證: ``` class Holiday < ActiveRecord::Base validates :name, uniqueness: { scope: :year, message: "should happen once per year" } end ``` 還有個 `:case_sensitive` 選項,指定唯一性驗證是否要區分大小寫,默認值為 `true`。 ``` class Person < ActiveRecord::Base validates :name, uniqueness: { case_sensitive: false } end ``` 注意,有些數據庫的設置是,查詢時不區分大小寫。 默認的錯誤消息是“has already been taken”。 #### 2.12 `validates_with` 這個幫助方法把記錄交給其他的類做驗證。 ``` class GoodnessValidator < ActiveModel::Validator def validate(record) if record.first_name == "Evil" record.errors[:base] << "This person is evil" end end end class Person < ActiveRecord::Base validates_with GoodnessValidator end ``` `record.errors[:base]` 中的錯誤針對整個對象,而不是特定的屬性。 `validates_with` 方法的參數是一個類,或一組類,用來做驗證。`validates_with` 方法沒有默認的錯誤消息。在做驗證的類中要手動把錯誤添加到記錄的錯誤集合中。 實現 `validate` 方法時,必須指定 `record` 參數,這是要做驗證的記錄。 和其他驗證一樣,`validates_with` 也可指定 `:if`、`:unless` 和 `:on` 選項。如果指定了其他選項,會包含在 `options` 中傳遞給做驗證的類。 ``` class GoodnessValidator < ActiveModel::Validator def validate(record) if options[:fields].any?{|field| record.send(field) == "Evil" } record.errors[:base] << "This person is evil" end end end class Person < ActiveRecord::Base validates_with GoodnessValidator, fields: [:first_name, :last_name] end ``` 注意,做驗證的類在整個程序的生命周期內只會初始化一次,而不是每次驗證時都初始化,所以使用實例變量時要特別小心。 如果做驗證的類很復雜,必須要用實例變量,可以用純粹的 Ruby 對象代替: ``` class Person < ActiveRecord::Base validate do |person| GoodnessValidator.new(person).validate end end class GoodnessValidator def initialize(person) @person = person end def validate if some_complex_condition_involving_ivars_and_private_methods? @person.errors[:base] << "This person is evil" end end # ... end ``` #### 2.13 `validates_each` 這個幫助方法會把屬性值傳入代碼庫做驗證,沒有預先定義驗證的方式,你應該在代碼庫中定義驗證方式。要驗證的每個屬性都會傳入塊中做驗證。在下面的例子中,我們確保名和姓都不能以小寫字母開頭: ``` class Person < ActiveRecord::Base validates_each :name, :surname do |record, attr, value| record.errors.add(attr, 'must start with upper case') if value =~ /\A[a-z]/ end end ``` 代碼塊的參數是記錄,屬性名和屬性值。在代碼塊中可以做任何檢查,確保數據合法。如果驗證失敗,要向模型添加一個錯誤消息,把數據標記為不合法。 ### 3 常用的驗證選項 常用的驗證選項包括: #### 3.1 `:allow_nil` 指定 `:allow_nil` 選項后,如果要驗證的值為 `nil` 就會跳過驗證。 ``` class Coffee < ActiveRecord::Base validates :size, inclusion: { in: %w(small medium large), message: "%{value} is not a valid size" }, allow_nil: true end ``` #### 3.2 `:allow_blank` `:allow_blank` 選項和 `:allow_nil` 選項類似。如果要驗證的值為空(調用 `blank?` 方法,例如 `nil` 或空字符串),就會跳過驗證。 ``` class Topic < ActiveRecord::Base validates :title, length: { is: 5 }, allow_blank: true end Topic.create(title: "").valid? # => true Topic.create(title: nil).valid? # => true ``` #### 3.3 `:message` 前面已經介紹過,如果驗證失敗,會把 `:message` 選項指定的字符串添加到 `errors` 集合中。如果沒指定這個選項,Active Record 會使用各種驗證幫助方法的默認錯誤消息。 #### 3.4 `:on` `:on` 選項指定什么時候做驗證。所有內建的驗證幫助方法默認都在保存時(新建記錄或更新記錄)做驗證。如果想修改,可以使用 `on: :create`,指定只在創建記錄時做驗證;或者使用 `on: :update`,指定只在更新記錄時做驗證。 ``` class Person < ActiveRecord::Base # it will be possible to update email with a duplicated value validates :email, uniqueness: true, on: :create # it will be possible to create the record with a non-numerical age validates :age, numericality: true, on: :update # the default (validates on both create and update) validates :name, presence: true end ``` ### 4 嚴格驗證 數據驗證還可以使用嚴格模式,失敗后會拋出 `ActiveModel::StrictValidationFailed` 異常。 ``` class Person < ActiveRecord::Base validates :name, presence: { strict: true } end Person.new.valid? # => ActiveModel::StrictValidationFailed: Name can't be blank ``` 通過 `:strict` 選項,還可以指定拋出什么異常: ``` class Person < ActiveRecord::Base validates :token, presence: true, uniqueness: true, strict: TokenGenerationException end Person.new.valid? # => TokenGenerationException: Token can't be blank ``` ### 5 條件驗證 有時只有滿足特定條件時做驗證才說得通。條件可通過 `:if` 和 `:unless` 選項指定,這兩個選項的值可以是 Symbol、字符串、`Proc` 或數組。`:if` 選項指定何時做驗證。如果要指定何時不做驗證,可以使用 `:unless` 選項。 #### 5.1 指定 Symbol `:if` 和 `:unless` 選項的值為 Symbol 時,表示要在驗證之前執行對應的方法。這是最常用的設置方法。 ``` class Order < ActiveRecord::Base validates :card_number, presence: true, if: :paid_with_card? def paid_with_card? payment_type == "card" end end ``` #### 5.2 指定字符串 `:if` 和 `:unless` 選項的值還可以是字符串,但必須是 Ruby 代碼,傳入 `eval` 方法中執行。當字符串表示的條件非常短時才應該使用這種形式。 ``` class Person < ActiveRecord::Base validates :surname, presence: true, if: "name.nil?" end ``` #### 5.3 指定 Proc `:if` and `:unless` 選項的值還可以是 Proc。使用 Proc 對象可以在行間編寫條件,不用定義額外的方法。這種形式最適合用在一行代碼能表示的條件上。 ``` class Account < ActiveRecord::Base validates :password, confirmation: true, unless: Proc.new { |a| a.password.blank? } end ``` #### 5.4 條件組合 有時同一個條件會用在多個驗證上,這時可以使用 `with_options` 方法: ``` class User < ActiveRecord::Base with_options if: :is_admin? do |admin| admin.validates :password, length: { minimum: 10 } admin.validates :email, presence: true end end ``` `with_options` 代碼塊中的所有驗證都會使用 `if: :is_admin?` 這個條件。 #### 5.5 聯合條件 另一方面,當多個條件規定驗證是否應該執行時,可以使用數組。而且,同一個驗證可以同時指定 `:if` 和 `:unless` 選項。 ``` class Computer < ActiveRecord::Base validates :mouse, presence: true, if: ["market.retail?", :desktop?] unless: Proc.new { |c| c.trackpad.present? } end ``` 只有當 `:if` 選項的所有條件都返回 `true`,且 `:unless` 選項中的條件返回 `false` 時才會做驗證。 ### 6 自定義驗證方式 如果內建的數據驗證幫助方法無法滿足需求時,可以選擇自己定義驗證使用的類或方法。 #### 6.1 自定義驗證使用的類 自定義的驗證類繼承自 `ActiveModel::Validator`,必須實現 `validate` 方法,傳入的參數是要驗證的記錄,然后驗證這個記錄是否合法。自定義的驗證類通過 `validates_with` 方法調用。 ``` class MyValidator < ActiveModel::Validator def validate(record) unless record.name.starts_with? 'X' record.errors[:name] << 'Need a name starting with X please!' end end end class Person include ActiveModel::Validations validates_with MyValidator end ``` 在自定義的驗證類中驗證單個屬性,最簡單的方法是集成 `ActiveModel::EachValidator` 類。此時,自定義的驗證類中要實現 `validate_each` 方法。這個方法接受三個參數:記錄,屬性名和屬性值。 ``` class EmailValidator < ActiveModel::EachValidator def validate_each(record, attribute, value) unless value =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i record.errors[attribute] << (options[:message] || "is not an email") end end end class Person < ActiveRecord::Base validates :email, presence: true, email: true end ``` 如上面的代碼所示,可以同時使用內建的驗證方法和自定義的驗證類。 #### 6.2 自定義驗證使用的方法 還可以自定義方法驗證模型的狀態,如果驗證失敗,向 `errors` 集合添加錯誤消息。然后還要使用類方法 `validate` 注冊這些方法,傳入自定義驗證方法名的 Symbol 形式。 類方法可以接受多個 Symbol,自定義的驗證方法會按照注冊的順序執行。 ``` class Invoice < ActiveRecord::Base validate :expiration_date_cannot_be_in_the_past, :discount_cannot_be_greater_than_total_value def expiration_date_cannot_be_in_the_past if expiration_date.present? && expiration_date < Date.today errors.add(:expiration_date, "can't be in the past") end end def discount_cannot_be_greater_than_total_value if discount > total_value errors.add(:discount, "can't be greater than total value") end end end ``` 默認情況下,每次調用 `valid?` 方法時都會執行自定義的驗證方法。使用 `validate` 方法注冊自定義驗證方法時可以設置 `:on` 選項,執行什么時候運行。`:on` 的可選值為 `:create` 和 `:update`。 ``` class Invoice < ActiveRecord::Base validate :active_customer, on: :create def active_customer errors.add(:customer_id, "is not active") unless customer.active? end end ``` ### 7 處理驗證錯誤 除了前面介紹的 `valid?` 和 `invalid?` 方法之外,Rails 還提供了很多方法用來處理 `errors` 集合,以及查詢對象的合法性。 下面介紹其中一些常用的方法。所有可用的方法請查閱 `ActiveModel::Errors` 的文檔。 #### 7.1 `errors` `ActiveModel::Errors` 的實例包含所有的錯誤。其鍵是每個屬性的名字,值是一個數組,包含錯誤消息字符串。 ``` class Person < ActiveRecord::Base validates :name, presence: true, length: { minimum: 3 } end person = Person.new person.valid? # => false person.errors.messages # => {:name=>["can't be blank", "is too short (minimum is 3 characters)"]} person = Person.new(name: "John Doe") person.valid? # => true person.errors.messages # => {} ``` #### 7.2 `errors[]` `errors[]` 用來獲取某個屬性上的錯誤消息,返回結果是一個由該屬性所有錯誤消息字符串組成的數組,每個字符串表示一個錯誤消息。如果字段上沒有錯誤,則返回空數組。 ``` class Person < ActiveRecord::Base validates :name, presence: true, length: { minimum: 3 } end person = Person.new(name: "John Doe") person.valid? # => true person.errors[:name] # => [] person = Person.new(name: "JD") person.valid? # => false person.errors[:name] # => ["is too short (minimum is 3 characters)"] person = Person.new person.valid? # => false person.errors[:name] # => ["can't be blank", "is too short (minimum is 3 characters)"] ``` #### 7.3 `errors.add` `add` 方法可以手動添加某屬性的錯誤消息。使用 `errors.full_messages` 或 `errors.to_a` 方法會以最終顯示給用戶的形式顯示錯誤消息。這些錯誤消息的前面都會加上字段名可讀形式(并且首字母大寫)。`add` 方法接受兩個參數:錯誤消息要添加到的字段名和錯誤消息本身。 ``` class Person < ActiveRecord::Base def a_method_used_for_validation_purposes errors.add(:name, "cannot contain the characters !@#%*()_-+=") end end person = Person.create(name: "!@#") person.errors[:name] # => ["cannot contain the characters !@#%*()_-+="] person.errors.full_messages # => ["Name cannot contain the characters !@#%*()_-+="] ``` 還有一種方法可以實現同樣地效果,使用 `[]=` 設置方法: ``` class Person < ActiveRecord::Base def a_method_used_for_validation_purposes errors[:name] = "cannot contain the characters !@#%*()_-+=" end end person = Person.create(name: "!@#") person.errors[:name] # => ["cannot contain the characters !@#%*()_-+="] person.errors.to_a # => ["Name cannot contain the characters !@#%*()_-+="] ``` #### 7.4 `errors[:base]` 錯誤消息可以添加到整個對象上,而不是針對某個屬性。如果不想管是哪個屬性導致對象不合法,只想把對象標記為不合法狀態,就可以使用這個方法。`errors[:base]` 是個數組,可以添加字符串作為錯誤消息。 ``` class Person < ActiveRecord::Base def a_method_used_for_validation_purposes errors[:base] << "This person is invalid because ..." end end ``` #### 7.5 `errors.clear` 如果想清除 `errors` 集合中的所有錯誤消息,可以使用 `clear` 方法。當然了,在不合法的對象上調用 `errors.clear` 方法后,這個對象還是不合法的,雖然 `errors` 集合為空了,但下次調用 `valid?` 方法,或調用其他把對象存入數據庫的方法時, 會再次進行驗證。如果任何一個驗證失敗了,`errors` 集合中就再次出現值了。 ``` class Person < ActiveRecord::Base validates :name, presence: true, length: { minimum: 3 } end person = Person.new person.valid? # => false person.errors[:name] # => ["can't be blank", "is too short (minimum is 3 characters)"] person.errors.clear person.errors.empty? # => true p.save # => false p.errors[:name] # => ["can't be blank", "is too short (minimum is 3 characters)"] ``` #### 7.6 `errors.size` `size` 方法返回對象上錯誤消息的總數。 ``` class Person < ActiveRecord::Base validates :name, presence: true, length: { minimum: 3 } end person = Person.new person.valid? # => false person.errors.size # => 2 person = Person.new(name: "Andrea", email: "andrea@example.com") person.valid? # => true person.errors.size # => 0 ``` ### 8 在視圖中顯示驗證錯誤 在模型中加入數據驗證后,如果在表單中創建模型,出錯時,你或許想把錯誤消息顯示出來。 因為每個程序顯示錯誤消息的方式不同,所以 Rails 沒有直接提供用來顯示錯誤消息的視圖幫助方法。不過,Rails 提供了這么多方法用來處理驗證,自己編寫一個也不難。使用腳手架時,Rails 會在生成的 `_form.html.erb` 中加入一些 ERB 代碼,顯示模型錯誤消息的完整列表。 假設有個模型對象存儲在實例變量 `@post` 中,視圖的代碼可以這么寫: ``` <% if @post.errors.any? %> <div id="error_explanation"> <h2><%= pluralize(@post.errors.count, "error") %> prohibited this post from being saved:</h2> <ul> <% @post.errors.full_messages.each do |msg| %> <li><%= msg %></li> <% end %> </ul> </div> <% end %> ``` 而且,如果使用 Rails 的表單幫助方法生成表單,如果某個表單字段驗證失敗,會把字段包含在一個 `&lt;div&gt;` 中: ``` <div class="field_with_errors"> <input id="post_title" name="post[title]" size="30" type="text" value=""> </div> ``` 然后可以根據需求為這個 `div` 添加樣式。腳手架默認添加的 CSS 如下: ``` .field_with_errors { padding: 2px; background-color: red; display: table; } ``` 所有出錯的表單字段都會放入一個內邊距為 2 像素的紅色框內。 ### 反饋 歡迎幫忙改善指南質量。 如發現任何錯誤,歡迎修正。開始貢獻前,可先行閱讀[貢獻指南:文檔](http://edgeguides.rubyonrails.org/contributing_to_ruby_on_rails.html#contributing-to-the-rails-documentation)。 翻譯如有錯誤,深感抱歉,歡迎 [Fork](https://github.com/ruby-china/guides/fork) 修正,或至此處[回報](https://github.com/ruby-china/guides/issues/new)。 文章可能有未完成或過時的內容。請先檢查 [Edge Guides](http://edgeguides.rubyonrails.org) 來確定問題在 master 是否已經修掉了。再上 master 補上缺少的文件。內容參考 [Ruby on Rails 指南準則](ruby_on_rails_guides_guidelines.html)來了解行文風格。 最后,任何關于 Ruby on Rails 文檔的討論,歡迎到 [rubyonrails-docs 郵件群組](http://groups.google.com/group/rubyonrails-docs)。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看