ruby-on-rails - Rails:NOT NULL Violation:保存明显的循环关联有时缺少外键
问题描述
将 Ruby-on-Rails 5.2.3 与 ruby 2.5 一起使用
问题:
我在 Rails 中编写了一个网络路由器配置前端,它有一些循环定义,比如这个:
- 转发有许多地址(被转发)。
- 一个地址有许多转发(转发给它)。
- 一个地址有许多转发(响应来自它)。
附录: 乍一看可能有点奇怪,这里有一张小图: 可以看到两个对象之间的三个不同的关联,其中每个关联中的“地址”扮演着完全不同的角色。(但他们可以改变角色,甚至扮演多个角色——这正是应用程序的重点:始终为终端设备生成语法和逻辑上正确的配置——这部分已经起作用了)。
另外,显然还需要:
- 一个地址属于一个接口。
我以通常的方式构建了它:
class Forward < ApplicationRecord
has_many :addresses, dependent: :nullify
belongs_to :remoteip, class_name: 'Address'
belongs_to :responding, class_name: 'Address', optional: true
end
class Address < ApplicationRecord
belongs_to :interface
belongs_to :forward, optional: true
has_many :remoteip_forwards, class_name: 'Forward',
foreign_key: :remoteip_id, dependent: :destroy
has_many :responding_forwards, class_name: 'Forward',
foreign_key: :responding_id, dependent: :nullify
end
然后数据库上有约束,因为对象不能是孤儿,在这种情况下:
- address.interface_id NOT NULL
然后我有一个顶级设计模型,它通过适当的关联将所有其他模型链接在一起,所以我可以抓住一个设计并将所有东西作为一棵树。
到目前为止,这一切都很好。但是当我尝试复制整个设计(使用deep_clone
gem)时,保存时,一些相当复杂的设计给了我NotNullViolation
.
分析:
日志显示 Rails 确实两次保存了一些 Address 记录:它执行了一次INSERT
with valid forward_id
but empty interface_id
,然后才执行了一次UPDATE
for the valid interface_id
,所有这些都在事务中。显然,这会触发NOT NULL
约束,并且操作失败(删除约束时,复制成功)。
如果实际设计确实包含循环引用,我会理解这一点(因为没有其他方法可以保存它),但是没有!只有在逻辑上才有可能使用这些模型创建循环引用。
我找不到有关 Rails 如何处理此类(可能)循环事物的适当文档。inverse_of
没有任何帮助(但这是可以预料的,因为我的命名是明确定义的)。
当我autosafe: false
在有问题的关联上使用时,问题得到解决,Rails 保存了树 - 但是没有两次引用的记录在副本中丢失(这并不奇怪)。
因此,遍历关联树以查找和保存记录的 Rails 代码似乎并不完全广泛,并且过早地使用两次写入记录(可能是为了提高性能?)。
如何以最好的方式解决这个问题?
- 可悲的是,
NOT NULL
约束不在DEFERRABLE
postgresql 中。太糟糕了,因为这似乎是最迷人的解决方案。 - 使用
autosave=false
,步行走关联树以找到正确的序列并单独保存每条记录?不,谢谢。 - 以不同的方式布局关联?如何?(我已经有了为目标设备转换所有数据的工作代码,并且适用于当前布局。)
- 取消 NOT NULL 约束?唔...
- 或者,Rails 中是否有可以调整的旋钮?
解决方案
推荐阅读
- asp.net - 小型医院的云托管?
- javascript - 在 ReactJS 中 onClick 时添加道具
- reactjs - Reactjs 中的 auto prettier 打破了界限
- excel - VBA在两个数字之间找到带小数的数字
- python-3.x - 无法在不同用户的 python 3.6.2 中创建 MSI 或 exe?
- html - 可以使用锚点代替 div 容器吗?
- angular - 我想在 ngModuleFactory 中运行 moduleType 并将 name 属性值带入
- c# - C# 中 SQL 时间戳的数据类型是什么?
- db2 - 由于数据库,应用程序不断崩溃
- android - 如何编写具有重复部分的应用程序?