【问题标题】:Does ActiveRecord handle looking up `enum`s by value when constructing the SQL query?在构造 SQL 查询时,ActiveRecord 是否处理按值查找“枚举”?
【发布时间】:2020-03-20 23:27:56
【问题描述】:

我在我的模型上定义了一个ActiveRecord enum,如下所示:

class ApiKey < ApplicationRecord
  enum api_version: { v1: -1, v2: 0, v3: 1 }
end

当我尝试通过枚举名称进行查询时,我注意到 ActiveRecord 没有正确构造查询。 例如

> ApiKey.where(owner_type: 'User', api_version: 'v3').to_sql
=> "SELECT \"api_keys\".* FROM \"api_keys\" WHERE \"api_keys\".\"owner_type\" = 'User' AND \"api_keys\".\"api_version\" = 0"

我指定了v3,所以我希望生成的查询能够正确地将其原始枚举值查找为1,但它使用的是0

当然,我可以自己进行枚举映射并让它产生正确的结果:

> ApiKey.where(owner_type: 'User', api_version: ApiKey.api_versions['v3']).to_sql
=> "SELECT \"api_keys\".* FROM \"api_keys\" WHERE \"api_keys\".\"owner_type\" = 'User' AND \"api_keys\".\"api_version\" = 1"

这是 ActiveRecord 中的一个错误,还是它不支持以这种方式查找枚举?我想它会因为枚举的全部意义在于能够使用友好/人类可读的名称。

谢谢!

注意事项

  • ApiKey#api_version 列的类型为 integer
  • 我使用的是 Postgres 9.5
  • 我正在使用activerecord-4.2.11.1

跟踪行为

以下是 ActiveRecord 源中发生的情况:

【问题讨论】:

  • 你试过用:v3代替'v3'吗?文档上的示例使用符号 edgeapi.rubyonrails.org/classes/ActiveRecord/Enum.html 我认为如果你想使用字符串,你必须将 { 'v1' =&gt; -1, 'v2' =&gt; ... } 定义为字符串
  • 使用:v3 有类似的问题。它命中同一行 -value.to_i rescue nil - 返回 nil 并在编译查询中呈现为 NULL
  • 不幸的是,将它们定义为String 键也无济于事。结果还是一样。

标签: ruby-on-rails postgresql ruby-on-rails-4 activerecord enums


【解决方案1】:

框架在https://github.com/rails/rails/blob/master/activerecord/lib/active_record/type_caster/connection.rb#L14中查找列类型,然后通过EnumType调用serialize:https://github.com/rails/rails/blob/master/activerecord/lib/active_record/enum.rb#L142

如果您将代码从字符串更改为符号(就像在枚举定义中编写的那样),它应该可以工作。

另一方面,最好不要使用数字枚举值,因为它们难以维护:数据只能在插入记录的应用程序的特定版本中读取。 由于您已经在使用 Postgres,请考虑使用正确的枚举类型。有像 pg_enumtorque 这样的优秀库来支持这项工作。

【讨论】:

  • 谢谢 - 我尝试用字符串键 (enum api_version: { 'v1' =&gt; -1, 'v2' =&gt; 0, 'v3' =&gt; 1 }) 定义它,但仍然没有运气。我最好的猜测是我正在使用v4.2,它似乎没有serialize() 方法(github.com/rails/rails/blob/v4.2.11.1/activerecord/lib/…)。
  • 啊,找到添加了通过serialize() 翻译枚举功能的提交。在我的版本 (v4.2.11.1) 中似乎不可用 - github.com/rails/rails/commit/…
  • 要添加到这一点并使枚举的引用更加动态,您应该使用此版本的查询ApiKey.where(owner_type: 'User', api_version: ApiKey.api_versions[:v3])。这将确保您不必担心枚举的位置会发生变化
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-12
  • 2012-06-16
  • 2022-10-21
  • 2011-10-14
  • 2017-03-31
相关资源
最近更新 更多