从Rails 4.1中的select保存枚举


108

我使用Rails 4.1中的枚举来跟踪葡萄酒的颜色。

Wine.rb

class Wine < ActiveRecord::Base
    enum color: [:red, :white, :sparkling]
end

在我看来,我会生成一个选择,以便用户可以选择某种颜色的葡萄酒

f.input :color, :as => :select, :collection => Wine.colors

这将生成以下HTML:

<select id="wine_color" name="wine[color]">
  <option value=""></option>
  <option value="0">red</option>
  <option value="1">white</option>
  <option value="2">sparkling</option>
</select>

但是,提交表单后,我收到了一个说明的参数错误'1' is not a valid color。我意识到这是因为color必须平等1而不是平等"1"

有没有一种方法可以迫使Rails将颜色解释为整数而不是字符串?

Answers:


203

好吧,显然,您不应该发送要保存的枚举的整数值。您应该发送枚举的文本值。

我将输入更改为以下内容:

f.input :color, :as => :select, :collection => Wine.colors.keys.to_a

其中生成了以下HTML:

<select id="wine_color" name="wine[color]">
  <option value=""></option>
  <option value="red">red</option>
  <option value="white">white</option>
  <option value="sparkling">sparkling</option>
</select>

值从“ 0”到“红色”,现在我们都设置好了。


如果您使用常规的ol's rails text_field,则为:

f.select :color, Wine.colors.keys.to_a


如果您希望拥有清晰易读的属性,也可以执行以下操作:

f.select :color, Wine.colors.keys.map { |w| [w.humanize, w] }


8
FWIW,我需要这样做,f.select :color, Wine.colors.to_a.map { |w| [w[0].humanize, w[0]] }因为它w代表一个数组。不知道为什么,但这也许会帮助某人。
jakealbaugh

titleize如果您有两个或两个以上的单词,使用一个更好的主意
Anwar 2015年

8
keys方法返回一个数组,因此to_a是多余的。或者,select helper方法使用2D数组作为选项,因此您可以使用to_a
xander-miller

如果发送了无效字符串,例如“ ERROR_JUNK_Submission”,该怎么办。显然,没有这样的枚举值,并且会引发异常。我们在哪里抓到它?
FlyingV

Wine模型具有一个称为color的枚举,enum color: [:red, :white, :sparkling]因此正确的句子是f.input :color, :as => :select, :collection => Wine.color.keys.to_a
hguzman

32

无需将枚举哈希转换为数组to_a。这足够了:

f.select :color, Wine.colors.map { |key, value| [key.humanize, key] }

10

我只是整理了一个EnumHelper,我想与他人分享以帮助需要更多自定义枚举标签和语言环境的人来选择您的枚举。

module EnumHelper

  def options_for_enum(object, enum)
    options = enums_to_translated_options_array(object.class.name, enum.to_s)
    options_for_select(options, object.send(enum))
  end

  def enums_to_translated_options_array(klass, enum)
    klass.classify.safe_constantize.send(enum.pluralize).map {
        |key, value| [I18n.t("activerecord.enums.#{klass.underscore}.#{enum}.#{key}"), key]
    }
  end

end

在您的语言环境中:

 en:
   activerecord:
     enums:
      wine:
        color:
          red:   "Red Wine"
          white:  "White Wine"

在您看来:

 <%= f.select(:color, options_for_enum(@wine, :color)) %>

1
编辑让翻译键被找到,而更典型的multiple_word_class_name下属于MultipleWordClassName而非multiplewordclassname枚举更具可读性
铁人无鬼

如何添加默认值以实现顺畅的备用广告:[I18n.t("activerecord.enums.#{klass.underscore}.#{enum}.#{key}", default: key.humanize), key] 此外,请删除最后一个,humanize因为如果多个单词可能会使大写字母失真
Peter P.

9

对于人类可读的,可接受的解决方案对我不起作用,但是我能够使它像这样工作:

<%= f.select(:color, Wine.colors.keys.map {|key| [key.humanize, key]}) %>

这是最干净的,但是我确实需要人性化我的密钥:

<%= f.select(:color, Wine.colors.keys) %>

1
rails 5现在支持最干净的方式
V-SHY

@ V-SHY,这是什么意思,您能解释一下吗?
ismailarilik

2
@ismailarilik,直接提供键作为选择选项,<%= f.select(:color,Wine.colors.keys)%>
V-SHY

6

如果在Rails 4中使用枚举,则只需调用Model.enums

f.select :color, Wine.colors.keys

创建HTML:

<select name="f[color]" id="f_color">
    <option value="red">red</option>
    <option value="white">white</option>
    <option value="sparkling"> sparkling </option>
</select>

或在控制器中添加方法:

def update_or_create
    change_enum_to_i
    ....
end

def change_enum_to_i
    params[:f]["color"] = params[:f]["color"].to_i
end

4

这是对我有效的Rails 4+:

class Contract < ApplicationRecord

enum status: { active:  "active", 
               ended: "active", 
               on_hold: "on_hold", 
               terminated:  "terminated", 
               under_review:  "under_review" , 
               unknown: "unknown" 
              }


end

在中my _form.html.erb,我有这个:

  <div class="field">
    <%= form.select :status, Contract.statuses.keys, {}%>
  </div>

添加记录后从控制台进行测试:

2.3.0 :001 > Contract.last.status
  Contract Load (0.2ms)  SELECT  "contracts".* FROM "contracts" ORDER BY "contracts"."id" DESC LIMIT ?  [["LIMIT", 1]]
 => "active"

3

如果您需要根据枚举键处理i18n,则可以使用:

<%= f.select :color, Wine.colors.keys.map {|key| [t("wine.#{key}"), key]} %>

并可以在翻译中设置颜色:

wine:
 red: Red
 white: White

1

这是我的解决方案(我的角色中带有下划线,例如“ sales_rep”),由于某种原因,这就是我需要一个空白选项才能工作的方式(使用simpleform?):

在ApplicationHelper中:

def enum_collection_for_select(attribute, include_blank = true)
  x = attribute.map { |r| [r[0].titleize, r[0]] }
  x.insert(0,['', '']) if include_blank == true
  x
end

然后以我的形式:

<%= f.input :role, collection: enum_collection_for_select(User.roles), selected: @user.role %>
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.