在我的Rails6.1.1应用程序中,我的Tweet型号有一个名为"PayLoads"的jsonb类型的列.我使用store直接访问此属性上的各个字段:

class Tweet < ApplicationRecord
  store :payload, accessors: [:lang, :text, :entities], coder: JSON
end

(请注意,第coder: JSON部分是将序列化从YAML缺省值更改为JSON所必需的,否则您将在jsonb列中使用YAML.)

当我创建一个新的tweet时,我发现ActiveRecord在插入期间错误地转义了JSON字符串:

Tweet.create payload: {foo: 'bar'}

TRANSACTION (0.7ms)  BEGIN
  Tweet Create (2.0ms)  INSERT INTO "tweets" ("payload", ...) VALUES ($1, ...) RETURNING "id"  [["payload", "\"{\\\"foo\\\":\\\"bar\\\"}\""], ...]

我指的是第"\"{\\\"foo\\\":\\\"bar\\\"}\""部分.看起来像是被双重逃脱了.这导致了一种"字符串异常",其中字符串中的一个字符串存储在payloads列中,使得postgres不能使用箭头->语法对字段执行任何搜索.Postgres不能将该值识别为JSON.(然而,看起来不可思议的是,Rails能够在读取操作中正确地反序列化该字段.)

另一个用户has illustrated的问题在此:https://dbfiddle.uk/gcwTQOUm

推荐答案

当我使用not使用store时,我无法重现这个问题:

Tweet.create payload: {foo: 'bar'}
  TRANSACTION (0.2ms)  BEGIN
  Tweet Create (1.6ms)  INSERT INTO "tweets" ("payload", ...) VALUES ($1, ...) RETURNING "id"  [["payload", "{\"foo\":\"bar\"}"], ...]

"{\"foo\":\"bar\"}"是所需的字符串.

这让我相信我用错了store.

我查了the docs:

注意:如果您使用的是 struct 化数据库数据类型(例如,PostgreSQL hstore/json或MySQL 5.7+json),则不需要.store提供的序列化.只需使用.store_accessor来生成访问器方法.请注意,这些列使用字符串键控哈希,不允许使用符号进行访问.

换句话说,因为我已经使用了一个jsonb类型的列,所以Rails已经知道要序列化散列--应用另一个序列化会导致双重转义.

因此,与其这样做,不如:

store :payload, accessors: [:lang, :text, :entities], coder: JSON

我知道要做什么:

store_accessor :payload, :lang, :text, :entities

而且很管用.

Ruby-on-rails相关问答推荐

使用FactoryBot测试ActiveStorage的RSpec请求规范

DateTime的最小数据提交格式

您如何测试方法调用块内调用的方法,以及使用 rspec 在块内传递给该方法调用的内容

为什么总是有东西在我的 mac 上的 5000 端口运行

ViewModel 和 Controller 有什么区别?

Rails 3 的data-method='delete'如何优雅地降级?

Ruby的自动代码质量工具?

如何在 to_json 中获取回形针图像的 url

在 JS 模块中使用 Rails-UJS(带有 webpacker 的 Rails 6)

Rails 上的 build 和 new 有什么区别?

Rails: WickedPDF: 分页符

Rails 4,使用 ActiveRecord 的原始查询

如何在 Rails 迁移中添加判断约束?

设计和强大的参数

Ruby on Rails:有条件地显示部分

如何在 Rails 迁移中添加一些插入?

如何在 Rails 应用程序中测试 ElasticSearch (Rspec)

如何在 Rails 迁移中判断数据库类型?

如何在 rake 任务中强制 RAILS_ENV?

使用连接的 Ruby on Rails ActiveRecord 查询