You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 05:40:23