Rails 5.0升级至5.2后build关联模型功能失效问题咨询
首先可以明确的是:build_association这个方法并没有被Rails移除,它在5.2版本中依然存在,你的问题根源出在关联方向的设计和Rails 5+对belongs_to的默认规则变化上。
先拆解下你的代码逻辑:
class User < ActiveRecord::Base belongs_to :profile, inverse_of: :user end class Profile < ActiveRecord::Base has_one :user, inverse_of: :profile end
这种关联意味着users表中存在profile_id字段,User是从属方,Profile是主方。当你调用new_user.build_profile时,确实会创建一个Profile实例,但此时这个Profile还未保存,它的id是nil,所以new_user.profile_id会被设为nil。
而从Rails 5.0开始,belongs_to关联默认开启了required: true验证——也就是User必须有对应的Profile才能保存。这就导致你调用new_user.save时,会因为profile_id为空而验证失败,最终既保存不了User,也保存不了Profile。
那为什么Rails 5.0中这段代码能运行?大概率是你当时要么手动关闭了required验证,要么关联方向是反过来的(User has_one :profile,Profile belongs_to :user),后者才是一对一关联更合理的设计。
推荐的解决办法:调整关联方向
把关联改成更符合逻辑的结构:让User作为主方,Profile作为从属方(外键放在profiles表的user_id字段),代码修改如下:
class User < ActiveRecord::Base has_one :profile, inverse_of: :user end class Profile < ActiveRecord::Base belongs_to :user, inverse_of: :profile end
这样你的原有代码new_user = User.new; new_user.build_profile; new_user.save就能正常工作了——保存User时,Rails会自动先保存关联的Profile,并把Profile的user_id设置为User的id,实现两者同时创建。
如果必须保留现有关联方向
如果你因为历史原因不能修改表结构,可以尝试两种方案:
- 关闭
belongs_to的必填验证,手动保存关联模型:class User < ActiveRecord::Base belongs_to :profile, inverse_of: :user, required: false end # 调用代码 new_user = User.new profile = new_user.build_profile new_user.save profile.save - 开启
autosave并调整保存顺序:class User < ActiveRecord::Base belongs_to :profile, inverse_of: :user, autosave: true, required: false end # 调用代码 new_user = User.new profile = new_user.build_profile profile.save # 先保存Profile拿到id new_user.profile_id = profile.id new_user.save
不过显然第一种方案更繁琐,还是推荐调整关联方向。
内容的提问来源于stack exchange,提问作者Brazhnyk Yuriy

