在构造 SQL 查询时,活动记录是否处理按值查找"枚举"?



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

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 源中发生的情况:

  • ActiveRecord::Relation#to_sql()中,它为每个绑定值调用connection.quote(..)(在本例中为Userv3(
  • ActiveRecord::ConnectionAdapters::Quoting#quote()中,它调用type_cast_for_database()来类型转换Integer
  • ActiveRecord::Type::Integer#type_cast_for_database()中,它调用type_cast()来执行实际的类型转换
  • ActiveRecord::Type::Value#type_cast()中,它调用cast_value()来执行特定于整数的类型转换
  • ActiveRecord::Type::Integer#type_cast_for_database()中,它调用value.to_i"v3"(字符串(转换为0(整数(

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

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

另一方面,最好不要使用数字枚举值,因为它们的维护有问题:数据只能通过插入记录的应用程序的特定版本读取。 由于您已经在使用 Postgres,请考虑使用适当的枚举类型。有像pg_enum和扭矩这样的好图书馆来支持这项工作。