【发布时间】:2014-03-08 17:36:58
【问题描述】:
所以我的数据库中有一个subscription 表。
我希望有一个 state 列,该列将具有以下任一值
Valid
Invalid
Cancelled
In Trial
Non Renewing
Future
有人可以解释如何在 rails 4 中将这些值用作枚举值吗?
【问题讨论】:
标签: ruby-on-rails ruby ruby-on-rails-4
所以我的数据库中有一个subscription 表。
我希望有一个 state 列,该列将具有以下任一值
Valid
Invalid
Cancelled
In Trial
Non Renewing
Future
有人可以解释如何在 rails 4 中将这些值用作枚举值吗?
【问题讨论】:
标签: ruby-on-rails ruby ruby-on-rails-4
归功于:https://hackhands.com/ruby-on-enums-queries-and-rails-4-1/
声明一个枚举属性,其中值映射到数据库中的整数,但可以通过名称进行查询。示例:
class Conversation < ActiveRecord::Base
enum status: [ :active, :archived ]
end
# conversation.update! status: 0
conversation.active!
conversation.active? # => true
conversation.status # => "active"
# conversation.update! status: 1
conversation.archived!
conversation.archived? # => true
conversation.status # => "archived"
# conversation.update! status: 1
conversation.status = "archived"
# conversation.update! status: nil
conversation.status = nil
conversation.status.nil? # => true
conversation.status # => nil
还将提供基于枚举字段允许值的范围。使用上面的例子,它将创建一个活动和存档的范围。
您可以从数据库声明中设置默认值,例如:
create_table :conversations do |t|
t.column :status, :integer, default: 0
end
好的做法是让第一个声明的状态成为默认状态。
最后,还可以使用哈希显式映射属性和数据库整数之间的关系:
class Conversation < ActiveRecord::Base
enum status: { active: 0, archived: 1 }
end
请注意,当使用数组时,从值到数据库整数的隐式映射是根据值在数组中出现的顺序得出的。在示例中,:active 映射为 0,因为它是第一个元素,而 :archived 映射为 1。一般情况下,第 i 个元素映射到数据库中的 i-1。
因此,一旦一个值被添加到枚举数组中,它在数组中的位置必须保持不变,新的值应该只添加到数组的末尾。要删除未使用的值,应使用显式 Hash 语法。
在极少数情况下,您可能需要直接访问映射。映射通过具有复数属性名称的类方法公开:
Conversation.statuses # => { "active" => 0, "archived" => 1 }
当您需要知道枚举的序数值时,使用该类方法:
Conversation.where("status <> ?", Conversation.statuses[:archived])
枚举属性的条件必须使用枚举的序数值。
更多信息:http://api.rubyonrails.org/v4.1.0/classes/ActiveRecord/Enum.html
【讨论】:
您可以使用名为 Workflow 的 gem。它使您能够使用自定义状态并优雅地处理状态之间的转换。我在许多 Rails3 和 Rails 4 应用程序中使用过它。
文档中的示例。
class Article
include Workflow
workflow do
state :new do
event :submit, :transitions_to => :awaiting_review
end
state :awaiting_review do
event :review, :transitions_to => :being_reviewed
end
state :being_reviewed do
event :accept, :transitions_to => :accepted
event :reject, :transitions_to => :rejected
end
state :accepted
state :rejected
end
end
后来:
article = Article.new
article.accepted? # => false
article.new? # => true
【讨论】:
编辑: 我在这里的回答不使用ActiveRecord magic enums。我在下面的建议适用于任何数据库库(如Sequel),我碰巧认为它更健壮。例如,它不依赖于值的自动排序,它使用 RDBMS 中的外键约束来确保您的值是有效的(与 ActiveRecord 解决方案不同)。但是,这不是 Rails 惯用的方法。请参阅 Giri 的答案(文档页面的副本/粘贴)以获得惯用答案。
在您的数据库中创建一个 states 表,其中填充了这些字符串值以及唯一 ID,例如
states
id | name
---+--------------
1 | Valid
2 | Invalid
3 | Cancelled
4 | In Trial
5 | Non Renewing
6 | Future
在 subscription 表中使用标准外键关联,引用来自 states 的条目。
在您的模型中,使用与 ID 匹配的常量创建一个 State 类,例如:
class State < ActiveRecord::Base
VALID = 1
INVALID = 2
# etc.
end
现在你 a) 保证你的表数据是有效的,并且 b) 你可以使用方便的引用,例如要求 subscriptions
Subscription.where( state_id: State::VALID )
【讨论】: