Rails预加载关联时保留build生成的未保存对象的方法
解决方案
问题本质
includes 会触发数据库查询并刷新关联集合,只保留从数据库加载的记录——你用build创建的无ID虚拟对象属于内存临时对象,会被这次刷新覆盖。
可行方案
1. 先预加载数据库数据,再添加虚拟对象
先完成关联数据的预加载,再将虚拟对象追加到关联集合中,避免被数据库查询覆盖:
# 先预加载数据库中的fields和conditions form = Form.find(params[:id]).includes(:fields, :conditions) # 再添加虚拟fields对象 form.fields.build(name: '虚拟字段1') form.fields.build(name: '虚拟字段2') # 此时form.fields同时包含数据库字段和虚拟字段
2. 自定义方法中手动合并数据库数据与虚拟对象
如果需要通过custom_fields这类自定义方法返回合并后的集合,可以在方法内先确保数据库数据已加载,再合并虚拟对象:
class Form < ApplicationRecord has_many :fields has_many :conditions def custom_fields # 优先使用已预加载的字段(避免重复查询),否则查询数据库 db_fields = fields.loaded? ? fields : fields.all # 合并虚拟字段(build创建的是内存对象,不会存入数据库) db_fields + [ fields.build(name: '虚拟字段1'), fields.build(name: '虚拟字段2') ] end end
注意:这里返回的是数组而非ActiveRecord::Relation,因为虚拟对象无法被纳入数据库查询的Relation中,若需保持链式查询能力,只能在数据库查询完成后再追加虚拟对象。
3. 暂存虚拟对象,预加载后重新添加
如果已经提前创建了虚拟对象,不想让后续的includes覆盖它,可以先暂存虚拟对象,预加载完成后重新添加:
form = Form.find(params[:id]) # 先创建虚拟对象并暂存 virtual_fields = [form.fields.build(name: '虚拟字段1'), form.fields.build(name: '虚拟字段2')] # 预加载数据库关联数据 form.includes(:fields, :conditions) # 重新添加虚拟对象 virtual_fields.each { |field| form.fields << field }
内容的提问来源于stack exchange,提问作者Ninh Le
相关产品推荐
相关产品推荐

