一、心得体会
1、今天完成了什么?
- Rails guide 4 170页 5个小时
- 重看了镐头书看了第一部分 1个小时
2、今天收获了什么?
- Active Record 数据验证、严格验证。。。
- 回调 before_create、before_save。。。
- 关联
- belongs_to 一对一
- has_one 一对一
- has_many 一对多
- has_and_belongs_to_many 多对多
- has_many :through 多对多
- has_one :through 一对一
3、今天状态如何?
- 今天状态爆表
4、今天犯了哪些错误?
- 今天吃完饭又撸了两把王者,回来之后,看了几篇文章
5、明天还有哪些工作需要完成?
- 明天要看镐头书的第二部分、第三部分
二、读书笔记
第三章 Active Record数据验证
本文介绍了如何使用Active Record提供的数据验证功能在数据存入数据库之前验证对象的状态。
- 如何使用AR内建的数据验证帮助方法
- 如何编写自定义的数据验证方法
- 如何处理验证时产生的错误消息
2、什么时候做数据验证?
Active Record使用实例方法new_record?判断对象是否已经存入数据库。
- create
- create!
- save
- save!
- update
- update!
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)
valid?和invalid?
Rails使用valid?方法检查对象是否合法。valid?方法会触发数据验证,如果对象上没有错误,就返回true,否则返回false,前面我们已经用过了:
class Person < ActiveRecord::Base
validates :name, presence: true
end
AR验证结束后,所有发现的错误都可以通过实例方法errors.message获取,该方法返回一个错误集合。如果数据验证后,这个集合为空,则说明对象是合法的。
注意,使用new方法初始化对象时,即使不合法也不会报错,因为这时还没与做数据验证。
errors[]
要检查对象的某个属性是否合法,可以使用errors[:attribute]中包含:attribute的所有错误。如果某个属性没有错误,就会返回空数组。
这个方法只在数据验证之后才能使用,因为它只是用来收集错误的,并不会触发验证。而且,和前面介绍的ActiveRecord::Base#invalid?方法不一样,因为erros[:attribute]不会验证整个对象,值检查对象的某个属性是否出错。
数据验证帮助方法
AR预先定义了很多数据验证帮助方法,可以直接在模型定义中使用,这些帮助方法提供了常用的验证规则,每次验证失败后,都会向对象的errors集合添加一个消息,这些消息和所验证的属性相关联。
每个帮助方法都可以接受任意数量的属性名,所以一行代码就能在多个属性上做同一种验证。
所有的帮助方法都可指定:on和:message选项,指定何时做验证,以及验证失败后向errors集合添加什么消息,:on选项的可选值是:create和:update。
acceptance
这个方法检查表单提交时,用户界面中的复选框是否被选中,这个功能一般用来要求用户接受程序的服务条款,阅读一些文字,等等。
class Person < ActiveRecord::Base
validates :name, acceptance: true
end
这个帮助方法的默认错误消息是“must be accepted”。
这个方法可以指定:accept选项,决定可接受什么值,默认是“1”,很容易修改。
class Person < ActiveRecord::Base
validates :name, acceptance: { accept: "yes"}
end
validates_associated
如果模型和其他模型有关联,也要验证关联的模型对象,可以使用这个方法,保存对象是,会在相关联的每个对象上调用valid?方法。
class Library < ActiveRecord::Base
has_many :books
validates_associated :books
end
这个帮助方法可用于所有关联类型。
不要在关联的两端都使用validates_associated,这样会生成一个循环。
validates_associated的默认错误消息是“is invalid”。注意,相关联的每个对象都有各自的errors集合,错误消息不会都集中在调用该方法的模型对象上。
confirmation
如果要检查两个文本字段的值是否相同,可以使用这个帮助方法,例如,确认Email地址或密码,这个帮助方法回传件一个虚拟属性。
class Library < ActiveRecord::Base
validates :email, confirmation: true
end
视图中这个写
<%= text_field :Library, :email_confirmation %>
只有email_confirmation的值不是nil时才会做这个验证。所以要确认属性加上存在性验证。
class Library < ActiveRecord::Base
validates :email, confirmation: true
validates :email_confirmation, presence: true
end
这个帮助方法的默认错误消息是“doesn't match confirmation”。
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”
format
这个帮助方法检查属性的值是否匹配:with选项指定的正则表达式。
class Account < ActiveRecord::Base
validate :legacy_code, format: { with: /\A[a-zA-Z]+\z/, message: "only allows letters" }
end
默认的错误消息是“is invalid”。
inclusion
这个帮助方法检查属性的值是否在指定的集合中。集合可以是任何一种可枚举的对象。
class Coffee < ActiveRecord::Base
validates :size, inclusion: { in: %w(small medium large), message: "%{value} is not a valid size" }
end
inclusion方法要指定:in选项,设置可接受哪些值。:in选项有个别名:with,作用相同。上面的例子设置了:message选项,演示如何获取属性的值。
该方法的默认错误消息是“is not included in the list”。
length
这个帮助方法验证属性值的长度,有多个选项,可以使用不同的方法指定长度限制:
class Person < ActiveRecord::Base
validates :name, length: {minimum: 2}
validates :bio, lenght: {maximum: 500}
validates :password, lenght: {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选项设置其他的统计方式:
numericality
这个帮助方法检查属性的值是否包含数字,默认情况下,匹配的值是可选的正负符号后加整数或浮点数,如果只接受整数,可以把:only_integer选项设为true。
/\A[+-]?\d+\Z/
否则,会尝试使用Float把值转换成数字。
presence
这个帮助方法坚持指定的属性是否为非空值,调用blank?方法检查是否为nil或空字符串。
absence
这个方法验证指定的属性是否为空,使用present?方法检测值是否为nil。
uniqueness
这个帮助方法会在保存对象之前验证属性值是否唯一。
validates_with
这个帮助方法把记录交给其他的类做验证。
validates_each
这个帮助方法会把属性值传入代码库做验证,没有预先定义验证的方式,你应该在代码库中定义验证方式。
常用的验证选项
:allow_nil
指定:allow_nil选项后,如果验证的值为nil就会跳过验证。
:allow_blank
:allow_blank选项和:allow_nil选项类似,如果验证的值为空,就会跳过验证。
:message
如果验证失败,会把:message选项指定的字符串添加到errors集合中。
:on
指定什么时候做验证。
严格验证
数据验证还可以使用严格验证模式,失败后会抛出ActiveModel::StrictValidationFailed异常。
条件验证
有时只有满足特定条件时做验证才说的通
指定Symbol
:if和:unless选项的值为Symbol时,表示要在验证之前执行对应的方法。
指定字符串
:if和:unless选项的值还可以是字符串
指定Proc
:if和:unless选项的值还可以是Proc
条件组合
有时同一个条件会用在多个验证上,这时可以使用with_options方法:
联合条件
如果是否做某个验证要满足多个条件时,可以使用数组,而且,都一个验证可以同时指定:if和:unless选项。
自定义验证方式
自定义验证使用的类
自定义验证使用的方法
还可以自定义方法验证模型的状态,如果验证失败,向errors集合添加错误信息,然后还要使用类方法validate注册这些方法。
处理验证错误
除了前面介绍的valid?和invalid?方法之外,Rails还提供了很多方法用来处理errors集合,以及查询对象的合法性。
errors[:base]
在视图中显示验证错误
Active Record 回调
- AR对象的生命周期
- 如何编写回调方法响应对象声明周期内发生的事件
- 如何把常用的回调封装到特殊的类中;
对象的生命周期
在Rails程序运行过程中,对象可以被创建、更新和销毁。Active Record为对象的生命周期提供了很多钩子,让你控制程序及其数据。
回调可以在对象的状态改变之前或之后触发指定的逻辑操作。
回调简介
注册回调
这种类方法还可以接受一个代码块,如果操作可以使用一行代码表述,可以考虑使用代码块形式。
class Person < ActiveRecord::Base
validates :login, :email, presence: true
before_create do
self.name = login.capitalize if name.blank?
end
end
注册回调时可以指定只在对象生命周期的特定事件发生时执行:
一般情况下,都把回调方法定义为受保护的方法或私有方法,如果定义成公共方法,回调就可以在模型外部调用。
可用的回调
创建对象
- before_validation
- after_validation
- before_save
- around_save
- before_create
- around_create
- after_create
- after_save
更新对象
- before_validation
- after_validation
- before_save
- around_save
after_initialize和after_find
after_initialize回调在Active Record对象初始化时执行,包括直接使用new方法初始化和从数据库中读取记录,after_initialize回调不用直接重定义Active Record的initialize方法。
after_find回调在数据库中读取记录时执行,如果同时注册了after_find和after_initialize
after_touch
after_touch回调在触碰Active Record对象时执行。
可以结合belongs_to一起使用
执行回调
下面的方法会触发执行回调
- create
- create!
跳过回调
和数据验证一样,回调也可跳过,使用下列方法即可:
- decrement
- decrement_counter
- delete
- delete_all
- increment
- increment_all
- toggle
- touch
- update_column
- update_columns
- update_all
终止执行
在模型中注册回调后,回调会加入一个执行队列。
关联回调
回调能在模型关联中使用,甚至可由关联定义,假如一个用户发布了多篇文章,如果用户删除了,他发布的文章也应该删除,下面我们在post模型中注册一个after_destroy回调,应用在User模型上:
条件回调
和数据验证类似,也可以满足指定条件时再调用回调方法。条件通过:if和:unless选项指定,选项的值可以是Symbol、字符串、Proc或数组。
使用Symbol
:if和:unless选项的值为Symbol时,表示要在调用回调之前执行对应的判断方法
使用字符串
使用Proc
回调的多重条件
回调类
事物回调
Active Record 关联
- 如何声明Active Record模型间的关联
- 怎么理解不同的Active Record关联类型
- 如何使用关联添加的方法
为什么要使用关联?
让代码更简洁
怎么使用关联?
- belongs_to 一对一关系
- has_one 一对一关系
- has_many 一对多关系
- has_many :through 多对多关系
- has_one :through 关联建立两个模型之间一对一关系,这种关联表示一个模型通过第三个模型拥有另一个模型的实例,例如,每个供应商只有一个账户,而且每个账户都有一个历史账户,那么定义模型:
- has_and_belongs_to_many
使用belongs_to还是has_one
如果想建立两个模型之间的一对一关系,可以在一个模型中声明belongs_to,然后再另一个模型中声明has_one。
使用has_many :through 还是has_and_belongs_to_many
如果需要做数据验证、回调,或者连接模型上要用到其他属性,此时就要使用has_many :through
第二种方法是使用has_many :through,但无法直接建立关联,要通过第三个模型
多态关联
关联还有一种高级用法,“多态关联”。在多态关联中,在同一个关联中,模型可以属于其他多个模型。例如,图片模型可以属于雇员模型或者产品模型,模型的定义如下:
自连接
设计数据模型时会发现,有时模型要和自己建立关联,例如,在一个数据表中保存所有雇员的信息,但要建立经理和下属之间的关系,这种情况可以使用自连接关联解决:
小技巧和注意事项
- 缓存控制
- 避免命名冲突
- 更新模式
- 控制关联的作用域
- Bi-directional associations
1、双向关联
默认情况下,AR并不知道这个关联中两个模型之间的关系,可能导致同一对象的两个副本不同步。
为了解决这个问题,引入了:inverse_of选项,可以告知Rails两者之间的关系。
2、关联详解
belongs_to 关联详解
belongs_to关联创建一个模型与另一个模型之间的一对一关系,用数据库的行话来说,就是这个类包含了外键。如果外键在另一个类中,就应该使用has_one关联。
belongs_to 关联添加的方法
如果关联的对象存在,associate方法会返回关联对象。如果找不到关联对象,则返回nil。
belongs_to方法的选项
Rails的默认设置足够智能,能满足常见需求,但有时还是需要定制belongs_to关联的行文,定制的方法很简单,声明关联时传入选项或者使用代码块即可。
belongs_to 关联支持以下选项:
- :autosave
- :class_name
- :counter_cache
belongs_to的作用域
检查关联的对象是否存在
什么时候保存对象
has_one关联详解
has_one关联添加的方法
- association(force_reload = false)
- association =(associate)
- build_association
- create_association(attributes = {})
has_one 方法的选项
has_one 的作用域
有时需要定制has_one关联使用的查询方式,定制的查询可在作用域代码块中指定。
检查关联对象是否存在
什么时候保存对象?
has_many 关联详解
has_many 关联添加的方法
声明has_many后,自动获得16个关联相关的方法:
- collection(force_reload = false)
- collection<<(object, ...)
- collection.delete(object, ...)
has_many 方法的选项
作用域
has_and_belongs_to_many 关联详解
关联回调
关联回调和普通回调都差不多,只不过集合生命周期中的事件触发的。关联回调有四种:
- before_add
- after_add
- before_remove
- after_remove
关联扩展
Rails基于关联代理对象自动创建的功能是死的,但是可以通过匿名模块、新的查询方法、创建对象的方法等进行扩展。