再次吐槽 Ruby China 对 Markdown 的支持: 排版的版本 http://vivijie.github.io/blog/2013/12/08/about-rails-validations/
今天做 Kevin gotealeaf 课后作业,有一个关于 validations 的题目:
** How exactly do Rails validations get triggered? Where are the errors saved? Why we need the ‘render’ to show the validation messages on the user interface? **
下面是我自己的理解,欢迎高手拍砖。
假设我们有一个 Category 类,变量是 name,在 model 文件里设置了 validates。
2.0.0-p247 :019 > Category
=> Category(id: integer, name: string, created_at: datetime, updated_at: datetime)
model/category.rb
class Category < ActiveRecord::Base
validates :name, presence: true
end
** 1. How exactly do Rails validations get triggered? **
查了 Rails 的文档,ActiveRecord::Base 中包括有:
ActiveRecord::AutosaveAssociation
ActiveRecord::Validations.
ActiveRecord::AutosaveAssociation 中有如下描述:
If validations for any of the associations fail, their error messages will be applied to the parent.
所以,如果没有对 name 进行赋值,save 的时候 ActiveRecord::AutosaveAssociation 发现 validations fail 了,产生了 error messages。
** 2. Where are the errors saved? **
2.0.0-p247 :023 > category.errors
=> #<ActiveModel::Errors:0x007fec3dbda728 @base=#<Category id: nil, name: "zoe", created_at: nil, updated_at: nil>, @messages={}>
发现 errors 是保存在@messages里面的。
下面看一下 messages 的内容是怎么产生的。
参照文档,自定义了 model 中的 validates。要求 name 首字母以 z 开头的时候,报错。
class Category < ActiveRecord::Base
validates_each :name do |record, attr, value|
record.errors.add attr, 'starts with z.' if value.to_s[0] == 'z'
end
end
在 console 中查看:
2.0.0-p247 :013 > category.name = 'zoe'
=> "zoe"
2.0.0-p247 :014 > category.valid?
=> false
2.0.0-p247 :015 > category.errors.messages
=> {:name=>["starts with z."]}
2.0.0-p247 :016 >
errors messages 修改成功。推断validates :name, presence: true
会生成默认的 messages:name:["can't be blank”]。我们可以自己写 validations 方法。
** 3. Why we need the ‘render’ to show the validation messages on the user interface? **
查了下 rails controller 的生命周期:
When your application receives a request, the routing will determine which controller and action to run, then Rails creates an instance of that controller and runs the method with the same name as the action.
在下一个页面请求的时候,controller 的生命周期结束。@messages是@controller的实例变量,会随着@controller的销毁而销毁。所以要用 render 来 hold 住页面,让页面不刷新。
** 不理解的地方: **
这里的 parent 指的是 errors 还是@messages呢?
<ActiveModel::Errors:0x007fec3dbda728 @base=#<Category id: nil, name: "zoe", created_at: nil, updated_at: nil>, @messages={}> messages={}>