为何子对象查找方式不同,父对象会因子对象无效出现不同验证结果?
问题原因分析
这是Rails Active Record关联对象跟踪机制导致的差异,核心在于子对象是否被父对象的关联缓存持有:
1. 方式一:父对象关联加载的子对象
当通过@client.default_phone获取Phone对象时,这个实例会被存入@client的**关联缓存(association_cache)**中。Rails会自动跟踪所有通过父对象关联加载且处于「脏状态(dirty)」的子对象:
- 修改该子对象属性(比如设置无效的
number)后,它会被标记为脏状态 - 调用
@client.invalid?时,Rails会自动检查所有已加载且脏的关联对象的验证状态——即便你没有显式添加validates_associated :default_phone - 子对象的验证错误会被合并到父对象的错误集合中,所以
@client返回invalid? => true,错误键包含:default_phone.number
2. 方式二:独立加载的子对象
通过@account.phones.find(...)获取的Phone对象,虽然对应数据库同一条记录,但这是一个全新的内存实例,并未被@client的关联缓存引用:
- 这个对象的脏状态和验证错误完全独立于
@client - 调用
@client.invalid?时,Rails不会检查这个无关实例,所以父对象仍返回valid? => true - 只有直接调用该子对象的
invalid?时,才会显示它自身的验证错误
验证机制的小技巧
可以通过以下代码验证两种场景的内存实例和关联缓存状态:
# 方式一: @client.default_phone.object_id == default_phone.object_id # => true @client.association(:default_phone).loaded? # => true # 方式二: @client.default_phone.object_id == default_phone.object_id # => false @client.association(:default_phone).loaded? # => false
额外说明
设置autosave: false不会改变这个行为——autosave控制的是保存父对象时是否自动保存关联对象,而验证时的关联跟踪是Rails主动处理已加载关联对象的逻辑,和autosave配置无关。
内容的提问来源于stack exchange,提问作者pinkfloyd90
相关产品推荐
相关产品推荐

