Rails 枚举 return 整数值,不是字符串表示

Rails Enum return integer value, not string representation

我有一个 table,其中包含一个基于整数的列(状态),我将其用于 Rails 模型中的枚举属性。

正在做的事情:

Post.select(:id, ..., :status)

定义为:

enum status: { inactive: 0, active: 1, ... }

它 returns 一切如预期,但状态列在其字符串值中返回为 inactiveactive,等。但我需要它作为一个整数。

我怎样才能得到它?

我目前正在使用 ActiveRecord::Base.connection.execute 并传递原始查询:

ActiveRecord::Base.connection.execute('select id, ..., status from posts')

是的。这就是枚举字段在 Rails 中的工作方式。数据存储为整数,但显示为字符串 - 基于枚举中定义的内容: https://api.rubyonrails.org/v5.2.3/classes/ActiveRecord/Enum.html

如果您真的想获得整数,请尝试以下方法之一:

  1. 删除枚举声明。数据存储为整数。如果没有该行,查询将 return Integer
  2. 这不是最漂亮的代码,但是:Post.statuses[post.status] 可以工作

你试过吗?

# Rails < 5
post = Post.find(123)
post.read_attribute(:status)

# Rails >= 5
post = Post.find(123)
post.read_attribute_before_type_cast(:status)

以上解决方案有效。我在 mysql 版本 5.7.28 上尝试使用原始 sql。 以下查询仅适用于 mysql。

我仍在寻找有关 postgres 的查询,我会尽快通知您。

 
create table Posts(id integer, title varchar(100), status ENUM ('active', 'inactive') NOT NULL);
insert into Posts(id, title, status) values(1, "Hello", 'inactive')
select title, status+0 from Posts; 

你可以试一试:

Post.select(:id, ..., 'status as int_status')

我不确定这是否直接适用,因为我不知道 Rails 或您的具体要求。但是你表示希望 "get a collection of records from the DB";所以也许会。
Postgres 在 pg_catalog 中包含 2 个表,您需要加入以获取枚举的集合值:pg_type 和 pg_enum 如下(其中 ENUM_NAME 被适当的枚举类型替换姓名:

select t.typname,e.enumsortorder, e.enumlabel
  from pg_type t 
  join pg_enum e 
    on (t.oid = e.enumtypid)
 where t.typname = 'ENUM_NAME'    
 order by e.enumsortorder;

我注意到的一个区别是,在 Postgres 中,关联的数值被定义为实数,而不是整数。这是由于值的实际维护方式。示例:

create type t_ord_stat as enum ('Ordered', 'Delivered', 'Billed', 'Complete', 'Canceled');

select e.enumsortorder, e.enumlabel
  from pg_type t 
  join pg_enum e 
    on (t.oid = e.enumtypid)
 where t.typname = 't_ord_stat'    
 order by e.enumsortorder;


alter type t_ord_stat add value 'Suppended: Out-of-Stock' after 'Ordered';
alter type t_ord_stat add value 'Suppended: Credit Check' before 'Suppended: Out-of-Stock' ; 
alter type t_ord_stat add value 'Suppended: Customer Refused Delivery' before 'Billed'; 

select e.enumsortorder, e.enumlabel
  from pg_type t 
  join pg_enum e 
    on (t.oid = e.enumtypid)
 where t.typname = 't_ord_stat'    
 order by e.enumsortorder;

上面给出了枚举字符串值的实际数值(因为列名表明它是为了排序目的)。您可以使用

获得相对整数值
select row_number() over() relative_seq, en.enumlabel
  from (select e.enumsortorder, e.enumlabel
  from pg_type t 
  join pg_enum e 
    on (t.oid = e.enumtypid)
 where t.typname = 't_ord_stat'    
 order by e.enumsortorder) en;

我会将 Rails 的实际转换留给您。希望能帮助到你。

您可以将 SQL 语句中的列映射到另一个字段并从那里读取它,这样 Rails 直接传递值,因为它不知道它是一个枚举(它根本不知道该列):

Post.select('id, title, status, status AS status_int').each do |post|
  puts post.status     # 'inactive'
  puts post.status_int # 0
end

如果您必须具有相同的列名,那么除非您做更多的工作,否则您就不走运了:

class Post
  STATUSES = %w(inactive active)
end

Post.all.each do |post|
  index = Post::STATUSES.index(post.status)
  # use 'index' here instead of `post.status`
end

如果这些都不够,那么这听起来很像 http://xyproblem.info/ ... 因为这些答案应该适用于 99% 的情况。因此,您可能应该解释为什么在处理对象时不能只使用 status_int,或者为什么不能将 int 存储在变量中,或者......为什么您根本需要访问整数,这会失败枚举的目的。

这不是最优雅的方式,但这是我认为无需遍历对象的唯一方式。

您可以像这样创建 2 个 classes 来实现此目的:

class Post < ApplicationRecord
end

另一个:

class PostSave < Post
  enum status: { inactive: 0, active: 1, ... }
end

当您使用 Post class 时,您不会获得 "status" 列的 enum 值,但是当您使用 PostSave class 您可以像以前一样使用 enum

现在当你

Post.select(:id, :status)

它将根据需要为 "status" 列提供整数值。