在构造 SQL 查询时,ActiveRecord 是否处理按值查找枚举?
Does ActiveRecord handle looking up `enum`s by value when constructing the SQL query?
我在我的模型上定义了一个 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 中的错误还是它不支持这种方式的枚举查找?我想它会因为枚举的全部意义在于能够使用 friendly/human 可读名称。
谢谢!
备注
ApiKey#api_version
列的类型为 integer
- 我正在使用 Postgres 9.5
- 我正在使用
activerecord-4.2.11.1
追踪行为
这是 ActiveRecord 源中发生的事情:
- 在
ActiveRecord::Relation#to_sql()
中,它为每个绑定值调用 connection.quote(..)
(在本例中为 User
和 v3
)
- 在
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 and then calls serialize via the EnumType
: https://github.com/rails/rails/blob/master/activerecord/lib/active_record/enum.rb#L142
中查找列类型
如果您将代码从字符串更改为符号(就像在您的枚举定义中所写的那样),它应该可以工作。
另一方面,最好不要使用数字枚举值,因为它们在维护时存在问题:数据只能通过插入记录的应用程序的特定版本读取。
由于您已经在使用 Postgres,请考虑使用适当的枚举类型。有像 pg_enum and torque 这样的优秀图书馆来支持这项工作。
我在我的模型上定义了一个 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 中的错误还是它不支持这种方式的枚举查找?我想它会因为枚举的全部意义在于能够使用 friendly/human 可读名称。
谢谢!
备注
ApiKey#api_version
列的类型为integer
- 我正在使用 Postgres 9.5
- 我正在使用
activerecord-4.2.11.1
追踪行为
这是 ActiveRecord 源中发生的事情:
- 在
ActiveRecord::Relation#to_sql()
中,它为每个绑定值调用connection.quote(..)
(在本例中为User
和v3
) - 在
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 and then calls serialize via the EnumType
: https://github.com/rails/rails/blob/master/activerecord/lib/active_record/enum.rb#L142
如果您将代码从字符串更改为符号(就像在您的枚举定义中所写的那样),它应该可以工作。
另一方面,最好不要使用数字枚举值,因为它们在维护时存在问题:数据只能通过插入记录的应用程序的特定版本读取。 由于您已经在使用 Postgres,请考虑使用适当的枚举类型。有像 pg_enum and torque 这样的优秀图书馆来支持这项工作。