将 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_clonegem)时,保存时,一些相当复杂的设计给了我NotNullViolation.
分析:
日志显示 Rails 确实两次保存了一些 Address 记录:它执行了一次INSERTwith valid forward_idbut empty interface_id,然后才执行了一次UPDATEfor the valid interface_id,所有这些都在事务中。显然,这会触发NOT NULL约束,并且操作失败(删除约束时,复制成功)。
如果实际设计确实包含循环引用,我会理解这一点(因为没有其他方法可以保存它),但是没有!只有在逻辑上才有可能使用这些模型创建循环引用。
我找不到有关 Rails 如何处理此类(可能)循环事物的适当文档。inverse_of没有任何帮助(但这是可以预料的,因为我的命名是明确定义的)。
当我autosafe: false在有问题的关联上使用时,问题得到解决,Rails 保存了树 - 但是没有两次引用的记录在副本中丢失(这并不奇怪)。
因此,遍历关联树以查找和保存记录的 Rails 代码似乎并不完全广泛,并且过早地使用两次写入记录(可能是为了提高性能?)。
如何以最好的方式解决这个问题?
- 可悲的是,
NOT NULL约束不在DEFERRABLEpostgresql 中。太糟糕了,因为这似乎是最迷人的解决方案。 - 使用
autosave=false,步行走关联树以找到正确的序列并单独保存每条记录?不,谢谢。 - 以不同的方式布局关联?如何?(我已经有了为目标设备转换所有数据的工作代码,并且适用于当前布局。)
- 取消 NOT NULL 约束?唔...
- 或者,Rails 中是否有可以调整的旋钮?