Rails 6.0.6创建Lot记录时传入关联ID仍查询NULL问题
问题描述
三年未维护的Rails应用原本正常运行,执行bundle install后,在Lot模型的register_new方法中创建实例时,已传入product_id、user_id、currency_id等关联ID,但调用save时触发了ID为NULL的关联查询,导致验证报错:
{:product=>["must exist"], :user=>["must exist"], :currency=>["must exist"]}
相关代码:
product.rb
class Product < ApplicationRecord belongs_to :company belongs_to :user belongs_to :sell_unit belongs_to :currency has_many :lots ...
currency.rb
class Currency < ApplicationRecord belongs_to :product ...
lot.rb
class Lot < ApplicationRecord belongs_to :product belongs_to :user belongs_to :currency ... def register_new ... x = self.new {:company_id=>2, :product_id=>1, :user_id=>2, :price=>"10000.0", :trigger_id=>2, :label=>"dd1f7a132e59", "quantity"=>"1", "expires_at"=>"2022-10-25 19:44:13 +0100", :currency_id=>1, :state_id=>5, :_active=>true, :activation_pending=>true} x.save
执行x.save时触发的SQL查询:
Product Load (0.3ms) SELECT `products`.* FROM `products` WHERE `products`.`id` = NULL LIMIT 1 ↳ app/models/lot.rb:54:in `register_new' User Load (0.2ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = NULL LIMIT 1 ↳ app/models/lot.rb:54:in `register_new' Currency Load (0.2ms) SELECT `currencies`.* FROM `currencies` WHERE `currencies`.`id` = NULL LIMIT 1 ↳ app/models/lot.rb:54:in `register_new'
问题原因与解决办法
核心原因
- Rails版本升级影响:执行
bundle install后,Rails版本大概率被更新到5.0及以上。该版本起,belongs_to关联默认强制验证关联对象存在,且验证逻辑会主动加载关联对象,而非仅检查外键字段是否非空。 - 参数键格式混合:创建实例时传入的参数同时使用了符号键(如
:product_id)和字符串键(如"quantity"),Rails参数解析时出现异常,导致部分外键字段未被正确赋值,最终触发查询时使用NULL作为ID。 - 循环关联冲突:Product与Currency存在双向
belongs_to的循环关联,新版本Rails的验证逻辑会因此引发额外加载冲突,加剧问题。
解决办法
- 统一参数键格式:将所有参数键统一为符号或字符串,避免混合。示例调整后代码:
x = self.new({ :company_id=>2, :product_id=>1, :user_id=>2, :price=>"10000.0", :trigger_id=>2, :label=>"dd1f7a132e59", :quantity=>"1", :expires_at=>"2022-10-25 19:44:13 +0100", :currency_id=>1, :state_id=>5, :_active=>true, :activation_pending=>true })
- 锁定Rails版本:若无需升级Rails,在
Gemfile中锁定原版本后重新执行bundle install。比如原版本为4.2.x:
gem 'rails', '~> 4.2.0'
- 调整关联验证规则:若保留新版本Rails,可在
belongs_to关联添加optional: true取消强制验证(业务允许的情况下):
class Lot < ApplicationRecord belongs_to :product, optional: true belongs_to :user, optional: true belongs_to :currency, optional: true ...
- 修复循环关联:Product与Currency的双向
belongs_to设计存在逻辑问题,建议重新梳理业务,将其中一个关联改为has_one或调整数据结构,避免验证死循环。
内容的提问来源于stack exchange,提问作者Bruno Esteves
相关产品推荐
相关产品推荐

