ActiveModel::Dirty 和 JSON 字段

ActiveModel::Dirty and JSON fields

我正在使用 ActiveModel::Dirty 来跟踪表单中所做的更改。现在一切都如我所料。对于像 phone 这样的数字,当我在表单中简单地格式化它们时,它们会在其中出现破折号,然后 phone 数字将不会出现在 .changed 的列表中,这是预期的行为。

但是我 运行 遇到了一个问题,我在我的配置文件模型中使用了 jsonb 字段。所以问题是 ActiveModel 会将 JSONB 字段列为已更改,即使我专门对其进行格式化以匹配它之前的样子。这不是预期的行为。更奇怪的是,我的另一个 JSONB 专栏并没有经历过这种疯狂。

我遇到问题的 JSONB 字段看起来像这样 store_accessor :user_details, :names, :other_field store_accessor :bank_details, :bank_city, :bank_name user_detailsbank_details 是 jsonb 列。 需要注意的一些事情:names 是一个数组,other_field 是一个字符串。 bank_city 和 bank_name 是字符串。

任何人都可以深入了解为什么 :user_details 特别是在努力解决这个问题而不是 :bank_details JSON 专栏吗?

我怀疑这可能是因为我在 :user_details 中使用了一个数组,而且我怀疑比较在 ActiveModel 源代码的某处被忽略了,但也许我错了?

编辑: 我发现这肯定是因为我正在为 :names 使用数组。我将其更改为字符串,它不再认为 JSON 列已更改。打算深入研究 ActiveModel 源代码,看看我是否能找到原因。

编辑#2: 出于某种原因,我认为我什么都不做就解决了这个问题,但我是个傻瓜,我意识到我已经删除了表格中的一些东西。所以这个问题对我来说仍然没有解决。任何见解都会令人惊叹。通过查看 ActiveModel::Dirty 源代码,我无法弄清楚为什么会发生这种情况。我不完全确定在哪里看。打算加入 byebugs 看看是否有帮助。

编辑 #3: 重复此问题的步骤

创建一个包含 JSONB 列的 rails 模型。 设置商店访问器,您只需要一个即可。 使用验证器或格式化程序将其默认设置为空数组。 为您的模型提供 ActiveModel::Dirty 包含。 运行 rails 控制台。 运行 以下命令。 假设 user_details 是 JSONB 列,它的存储访问器是 names.

  a = Profile.user_details
  a.user_details = { "names" => [{"first_name" => "", "last_name" => "" }] } # This is to replicate what it would look like in a form when a user is submitting a blank entry.
  a.changed # This will show that user_details has changed which is correct
  a.names = []
  a.changed # This will still show that user_details has changed even though it has been set back to its initial state of an empty array. This would work if it was a string field instead of an array.

在 Rails Github 上打开一个问题后,我得到了回复 https://github.com/rails/rails/issues/34537#issuecomment-442265161

更改是通过 json、jsonb、hstore 和序列化属性类型的属性强制进行的。源代码在这里:https://github.com/rails/rails/blob/06ab7b27ea1c1ab357085439abacdb464f6742bf/activerecord/lib/active_record/store.rb#L181

我遇到这个问题的原因是我不再尝试尝试,因为我正在进行的项目不会使用 ActiveModel::Dirty 并且不会尝试跟踪改变了我尝试这样做的方式。

所以遇到这个问题的任何后代祝你好运,并随时进入那个 github 问题并抱怨这不起作用。