使用 update_all 在 Rails 中存储

问题描述 投票:0回答:1

在我们的模型之一中,我们使用 ActiveRecord 的存储,如下所示:

store :settings, accessors: %i[enabled unit_of_distance], coder: JSON

这允许我们像普通列一样访问这些访问器,例如

Model.first.enabled => true
Model.first.unit_of_distance => 'miles'

如果我想像这样更新所有模型:

Model.update_all(unit_of_distance: 'miles')

我收到错误:

ActiveRecord::StatementInvalid: Mysql2::Error: Unknown column 'model.unit_of_distance' in 'field list'

像这样用

settings
前缀可以修复它,但随后会覆盖
settings
中的其他访问器(这根本不是我们想要发生的情况):

Model.update_all(settings: { unit_of_distance: 'miles' })

但是手动循环它们确实有效:

Model.all.each { |c| c.update(unit_of_distance: 'miles') }

但这可能效率不高...是否可以将

update_all
与存储访问器一起使用而无需执行循环?

ruby-on-rails activerecord
1个回答
0
投票

这里有一个漏洞抽象与一把巨大的脚枪相结合。

ActiveRecord 试图跨越基于表的数据库世界和应用程序中的对象之间的鸿沟,但像任何 ORM 一样会遇到挫折。

update_all
是在 SQL 世界中操作的方法之一,而不是像
update
那样在对象上操作。因此,期望它采用哈希值来更新 JSON 列必然会导致失望。
store
是一把脚枪,它将序列化数据存储为字符串,然后在应用程序中将其反序列化 - 在面向对象的世界中。

store
是 RDMBS 系统拥有 JSON 和 JSONB 等结构化数据类型之前黑暗时代的一个老黑客。如果将其与本机 JSON 列一起使用,您将存储垃圾数据,因为您得到的是转义字符串而不是 JSON 对象。它带有以下警告:

注意:如果您使用结构化数据库数据类型(例如 PostgreSQL hstore/json,或 MySQL 5.7+ json)不需要 .store 提供的序列化。只需使用 .store_accessor 即可 生成访问器方法。

解决问题的第一步是修复表中的任何现有数据,并将存储的字符串转换为可以由数据库查询的实际 JSON。您可以使用

update_all
数据库 JSON 函数 来完成此操作,或者如果性能不是问题的话,可以通过循环遍历 Ruby 中的数据来完成此操作。

如果您想坚持将应该是列的内容放入 JSON 列中,您将需要使用 MySQL 特定的 JSON 运算符。

Model.update_all('model.settings = JSON_MERGE_PATCH(
  model.settings, \'{"unit_of_distance": "miles" }\'
)')

如果您的数据足够结构化,以至于您可以在模型中为其创建访问器,那么它的非结构化程度不足以保证使用 JSON。

© www.soinside.com 2019 - 2024. All rights reserved.