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

Ruby on Jets中ActiveRecord单表继承type字段为空报错问题

问题描述

使用Ruby on Jets开发时,定义了父类模型:

class Transfer < ActiveRecord::Base; end

以及两个命名空间下的子类:

class Transfers::External < Transfer; end

class Transfers::Internal < Transfer; end

尝试保存Transfers::Internal实例时触发非空约束错误,提示type字段为空:

#> t = Transfers::Internal.new
#> t.save
ActiveRecord::StatementInvalid (PG::NotNullViolation: ERROR: null value in column "type" violates not-null constraint
#> t.type
nil
问题原因

这是ActiveRecord单表继承(STI)处理命名空间子类时的典型问题,核心原因如下:

  • ActiveRecord的STI机制依赖type字段存储子类类名,实现单表多态查询。但子类处于嵌套命名空间(如Transfers::Internal)时,默认STI自动赋值逻辑可能因命名空间解析问题失效,导致实例type属性无法自动填充。
  • Jets的模型加载顺序若导致子类在父类之前被加载,会破坏STI初始化逻辑,父类无法识别子类继承关系,进而无法自动设置type字段值。
解决建议

针对该问题,可通过以下两种方式修复:

  1. 在子类中显式指定STI名称
    直接在子类中定义sti_name,明确告知ActiveRecord要写入type字段的值:
class Transfers::Internal < Transfer
  self.sti_name = "Transfers::Internal"
end

class Transfers::External < Transfer
  self.sti_name = "Transfers::External"
end
  1. 在父类中配置STI类名解析
    若希望type字段存储短名称(如"Internal"而非完整命名空间类名),可在父类中重写find_sti_class方法实现类名映射:
class Transfer < ActiveRecord::Base
  def self.find_sti_class(type_name)
    full_class_name = "Transfers::#{type_name}"
    super(full_class_name)
  end
end

# 同时在子类中指定短名称
class Transfers::Internal < Transfer
  self.sti_name = "Internal"
end

内容的提问来源于stack exchange,提问作者Thiago Diniz

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 17:14:56