The Ruby On Rials Gudie -- Active Record Validations

Active Record 验证

就是像下面的似的东西

class Person < ActiveRecord::Base
   validates :name , presence: true
end
 
Person.create(name: "John Doe" ).valid? # => true
Person.create(name: nil ).valid? # => false

它是用来保证只有合法的数据才能够进入你的数据库,model层的validation(这里类似于migration 我们多使用英文)是最好的防止用户非法数据进入数据库的方法。


当我们new一个东西的时候,他并没有写入到数据库中,只有我们执行save时才会写入的。我们可以利用new_record来判断是否写入了数据库。而我们的validate就是发生在创建object之后,写入数据库之前。假如validate不通过,那么就返回一个invalid,并且不写入数据库。

下面这些函数只有数据valid的时候才会写入数据库,感叹号和非感叹号的区别就是在于感叹号数据非法时抛异常,而非感叹号返回false或者object本身

  • create
  • create!
  • save
  • save!
  • update
  • update!
而下面这些函数则是忽略validation判断的,直接写入数据库,也不管是不是合法

  • decrement!
  • decrement_counter
  • increment!
  • increment_counter
  • toggle!
  • touch
  • update_all
  • update_attribute
  • update_column
  • update_columns
  • update_counters
  • save(validate: false)
这里save用时参数加上这个就可以逃过validation判断


如何判断object是否valid呢,我们使用的方法是valid?和invalid?用法如下

class Person < ActiveRecord::Base
   validates :name , presence: true
end
 
Person.create(name: "John Doe" ).valid? # => true
Person.create(name: nil ).valid? # => false

假如create的非法,我们需要知道错误信息,那么我们就要使用errors返回所有的错误信息了,他是一个hash,key为错误的列的名字,data为错误信息。所以我们可以用errors[:name]得到关于name的错误信息。假如我们的create合法,那么errors为空

class Person < ActiveRecord::Base
   validates :name , presence: true
end
 
>> Person. new .errors[ :name ].any? # => false
>> Person.create.errors[ :name ].any? # => true

Validation Helpers


上面讲的很多东西都很是理论的,下面讲的东西将是真正的一些函数的使用。注意,helper在rails中有小工具的意思,你可以直接使用。

这些helper有一个共同的特点,他们都在你的model上添加上相应的validate,假如不通过那么就会在errors中加入错误信息。并且他们都可以接受:on 和 :message 两个参数,on参数决定这个validation是施加在哪个操作上面的,默认的是:save 还可以是 :create 或者 :update ;而 :message参数则决定发生错误往errors里面写入的信息


他们的形式都是类似这样子的

 validates:terms_of_service, acceptance: true

validates 调用函数,:terms_of_service 是你列的名字,而acceptance:true是一个key =》 data 键值对形式,表明了helper使用的类型,下面一个个的说说

acceptance
 这个申请某个变量可接受的数据,它并不写入数据库,可以参考我们要求用户同意某种服务款项什么的,要求用户同意,但是我们不用把它写入数据库。

validates:terms_of_service, acceptance: true

这个表明terms_of_service 这个参数只接受true


class Person < ActiveRecord::Base
   validates :terms_of_service , acceptance: { accept: 'yes' }
end

而这个表明terms_of_service接受yes这个字符串,也就是说们key => data(这个是个hash对,ruby中可以用key:data表示)data也可以是一个hash,里面的accept key 表明了可以接受的参数形式


validates_associated
用来表示你的模型和其他模型有关联关系,你的模型包含着其他的模型,就例如下面的

class Library < ActiveRecord::Base
   has_many :books
   validates_associated :books
end
但我们save Library 模型时,也会调用Book模型的save函数,同样的道理适用于valid? 这个函数,注意不要再Book中也加入libraries 的associate ,因为这样会成循环,而且是死循环的。


confirmation
class Person < ActiveRecord::Base
   validates :email , confirmation: true
end

这个是什么意思呢,他就是我们网页中需要输入两次相同信息的部分,比如初始化密码的设置,需要两次输入的一样什么的。它会自动和email_confirmation 这个参数比较

我们在view层可以这样用

<%= text_field :person , :email %>
<%= text_field :person , :email_confirmation %>

而在model定义的时候,我们要加上这个

class Person < ActiveRecord::Base
   validates :email , confirmation: true
   validates :email_confirmation , presence: true
end

就是简单的加上了email_confirmation必须出现


exclusion

除了的意思,验证数据的值没有在参数里面的数据出现。

class Account < ActiveRecord::Base
   validates :subdomain , exclusion: { in : %w(www us ca jp),
     message: "Subdomain %{value} is reserved." }
end
这个意思是subdomain函数不能出现www、us、ca或者jp %w是创建了一个字符串数据。exclusion指向一个hash,这个hash里面有一个key 叫in 它指向一个数组,这个数组中的数据都是非法参数。这里in,可以使用within 是一样的,他们仅仅是别名的关系


inclusion
和上面的整好相反,数据的值要出现在参数的数据中,用法相同,同样in 可以换位within

class Coffee < ActiveRecord::Base
   validates :size , inclusion: { in : %w(small medium large),
     message: "%{value} is not a valid size" }
end

format
定义可接受数据的格式

class Product < ActiveRecord::Base
   validates :legacy_code , format: { with: /\ A [a-zA- Z ]+\z/,
     message: "Only letters allowed" }
end

同样format指向一个hash,里面有一个with key 它指向一个正则表达式 


length
这个是参数可接受的长度,这个并不一定是固定长度,他也是指向一个hash,可以进行各种设置

class Person < ActiveRecord::Base
   validates :name , length: { minimum: 2 } #最小
   validates :bio , length: { maximum: 500 } #最大
   validates :password , length: { in : 6 .. 20 } #在6-20范围内
   validates :registration_number , length: { is: 6 } #恰好长度为6
end

length指向的hash 主要包含以上几种,可以同时使用,用逗号分开即可。

它的错误信息除了可以用:message指定外,还可以用:wrong_length 、 too_long 、too_short指定,再这些信息中可以使用%{count}代替指定的长度,在too_long中它就显示最大的长度,too_short中就显示最小的长度。

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" , #count = 300
     too_long: "must have at most %{count} words" #count = 400
   }
end

numericality
它约定你的参数只能是数字,这里数字指整数和浮点数。假如你numericality指向一个hash,里面only_integer 设置为true,那么他就仅接受整数了。

用法如下

class Player < ActiveRecord::Base
   validates :points , numericality: true
   validates :games_played , numericality: { only_integer: true }
end

除了only_integer,他还接受

  • :greater_than - 大于
  • :greater_than_or_equal_to - 大于等于
  • :equal_to - 等于
  • :less_than -小于
  • :less_than_or_equal_to -小于等于
  • :odd - 如果设置为true,那么它仅接受奇数
  • :even -如果设置为true,那么它仅接受偶数
presence
不能为空

class Person < ActiveRecord::Base
   validates :name , :login , :email , presence: true
end

absence
为空

class Person < ActiveRecord::Base
   validates :name , :login , :email , absence: true
end

uniqueness
唯一的,他并不是给数据库列加上唯一约束,而是通过在存储前查找实现的。

class Account < ActiveRecord::Base
   validates :email , uniqueness: true
end

 

uniqueness也可以指向一个hash,常用的是忽略大小写敏感用case_sensitive

class Person < ActiveRecord::Base
   validates :name , uniqueness: { case_sensitive: false }
end


Common Validation Options


一般的validation选项

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

allow_blank
允许blank,blank是rails中的函数,“”.blank? == true, "".nil? == flase 这是blank?和nil?最大的区别,blank?关心的是内容,而nil更注重这个object是否存在。

class Topic < ActiveRecord::Base
   validates :title , length: { is: 5 }, allow_blank: true
end
 
Topic.create( "title" => "" ).valid?  # => true
Topic.create( "title" => nil ).valid? # => true


strict validations

假如我们需要在validation不满足时不仅仅是一个异常,而是抛出一个错误,我们可以设置:strict为true,当然我们还可以用strict来设置自己的异常处理函数。当不满足validation时我们自行进行处理。

class Person < ActiveRecord::Base
   validates :name , presence: { strict: true }
end
 
Person. new .valid?  # => ActiveModel::StrictValidationFailed: Name can't be blank


class Person < ActiveRecord::Base
   validates :token , presence: true , uniqueness: true , strict: TokenGenerationException
end
 
Person. new .valid?  # => TokenGenerationException: Token can't be blank


Conditional Validation

条件validation,就是使用if和unless判断断言满足或者不满足。

5.1 Using a Symbol with :if and :unless

You can associate the :if and :unless options with a symbol corresponding to the name of a method that will get called right before validation happens. This is the most commonly used option.

这个主要说的是你可以用一个函数的符号放在if后面,然后你在声明这个函数,这个函数需要返回true或false

class Order < ActiveRecord::Base
   validates :card_number , presence: true , if : :paid_with_card ?
 
   def paid_with_card?
     payment_type == "card"
   end
end
5.2 Using a String with :if and :unless

You can also use a string that will be evaluated using eval and needs to contain valid Ruby code. You should use this option only when the string represents a really short condition.

你可以在if后面放一个string,这个string可以是一段可以在irb执行的ruby代码

class Person < ActiveRecord::Base
   validates :surname , presence: true , if : "name.nil?"
end
5.3 Using a Proc with :if and :unless

Finally, it's possible to associate :if and :unless with a Proc object which will be called. Using a Proc object gives you the ability to write an inline condition instead of a separate method. This option is best suited for one-liners.

可以在后面放置一个proc对象。

class Account < ActiveRecord::Base
   validates :password , confirmation: true ,
     unless : Proc . new { |a| a.password.blank? }
end
5.4 Grouping Conditional validations

Sometimes it is useful to have multiple validations use one condition, it can be easily achieved using with_options.

还可以将所有的if或者unless约束打组写出,以前我们一个个为参数约定变量时,我们用validates,现在我们可以使用with_options 对所有需要if或者unless的参数打组约定。这样,所有在with_options中的参数都会自动的加上if: :is_admin?约束(就是指向符号的约束)。

class User < ActiveRecord::Base
   with_options if : :is_admin ? do |admin|
     admin.validates :password , length: { minimum: 10 }
     admin.validates :email , presence: true
   end
end

All validations inside of with_options block will have automatically passed the condition if: :is_admin?

5.5 Combining Validation Conditions

On the other hand, when multiple conditions define whether or not a validation should happen, an Array can be used. Moreover, you can apply both :if and :unless to the same validation.

假如你有多个if或者unless约束,你可以将他们放在一个数组中,然后用一个if或者unless约束就好啦

class Computer < ActiveRecord::Base
   validates :mouse , presence: true ,
                     if : [ "market.retail?" , :desktop ?]
                     unless : Proc . new { |c| c.trackpad.present? }
end

The validation only runs when all the :if conditions and none of the :unless conditions are evaluated to true.

Performing Custom Validations

当默认的validations helper无法满足你的需要时,你可以定义自己的validation。用法很简单,你需要继承ActiveModel::Validator类重写其中的validate 方法。你约束时用validates_with  就可以了,在后面写上你自己定义的class名字。validate函数返回的erros


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

很简单吧,其实还有个更简单的办法,你可以像一般函数一样使用这些自定义的validations。就是你继承自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

你看他的明明方式还是很有特点的,你的class 叫 XValidator 那么你的约束就是叫 x 比如 EmailValidator 就是:email


最后,一些没讲的东西,我感觉用处不大的,我没有再翻译,仅粘贴英文


6.2 Custom Methods(这个是指直接用方法约束你的参数)

You can also create methods that verify the state of your models and add messages to the error scollection when they are invalid. You must then register these methods by using the validate class method, passing in the symbols for the validation methods' names.

You can pass more than one symbol for each class method and the respective validations will be run in the same order as they were registered.

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

By default such validations will run every time you call valid?. It is also possible to control when to run these custom validations by giving an :on option to the validate method, with either: :createor :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 Working with Validation Errors(各种errors的操作)

In addition to the valid? and invalid? methods covered earlier, Rails provides a number of methods for working with the errors collection and inquiring about the validity of objects.

The following is a list of the most commonly used methods. Please refer to the ActiveModel::Errors documentation for a list of all the available methods.

7.1 errors

Returns an instance of the class ActiveModel::Errors containing all errors. Each key is the attribute name and the value is an array of strings with all 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 = Person. new (name: "John Doe" )
person.valid? # => true
person.errors # => []
7.2 errors[]

errors[] is used when you want to check the error messages for a specific attribute. It returns an array of strings with all error messages for the given attribute, each string with one error message. If there are no errors related to the attribute, it returns an empty array.

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

The add method lets you manually add messages that are related to particular attributes. You can use the errors.full_messages or errors.to_a methods to view the messages in the form they might be displayed to a user. Those particular messages get the attribute name prepended (and capitalized). add receives the name of the attribute you want to add the message to, and the message itself.

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 !@#%*()_-+="]

Another way to do this is using []= setter

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]

You can add error messages that are related to the object's state as a whole, instead of being related to a specific attribute. You can use this method when you want to say that the object is invalid, no matter the values of its attributes. Since errors[:base] is an array, you can simply add a string to it and it will be used as an error message.

class Person < ActiveRecord::Base
   def a_method_used_for_validation_purposes
     errors[ :base ] << "This person is invalid because ..."
   end
end
7.5 errors.clear

The clear method is used when you intentionally want to clear all the messages in the errorscollection. Of course, calling errors.clear upon an invalid object won't actually make it valid: the errors collection will now be empty, but the next time you call valid? or any method that tries to save this object to the database, the validations will run again. If any of the validations fail, the errors collection will be filled again.

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

The size method returns the total number of error messages for the object.

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 Displaying Validation Errors in Views

Once you've created a model and added validations, if that model is created via a web form, you probably want to display an error message when one of the validations fail.

Because every application handles this kind of thing differently, Rails does not include any view helpers to help you generate these messages directly. However, due to the rich number of methods Rails gives you to interact with validations in general, it's fairly easy to build your own. In addition, when generating a scaffold, Rails will put some ERB into the _form.html.erb that it generates that displays the full list of errors on that model.

Assuming we have a model that's been saved in an instance variable named @post, it looks like this:

<% 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 %>

Furthermore, if you use the Rails form helpers to generate your forms, when a validation error occurs on a field, it will generate an extra <div> around the entry.

<div class="field_with_errors">
  <input id="post_title" name="post[title]" size="30" type="text" value="">
</div>

You can then style this div however you'd like. The default scaffold that Rails generates, for example, adds this CSS rule:

.field_with_errors {
   padding: 2px;
   background-color: red;
   display: table;
}

This means that any field with an error ends up with a 2 pixel red border.


 









  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值