Сохранение enum из select в Rails 4.1

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 интерпретировать цвет как целое число, а не строку?

Брайан Вайнрайх
источник

Ответы:

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 'rails, это:

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может быть лучшей идеей, если у вас есть два или более слов
Анвар
8
keysметод возвращает массив таким образом , to_aявляется избыточным. В качестве альтернативы вспомогательный метод select принимает 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
Редактирование делает ключи перевода более удобочитаемыми за счет нахождения перечислений, принадлежащих MultipleWordClassName, под довольно типичным именем multiple_word_class_name, а не с
именем
Как насчет добавления значения по умолчанию для изящного отката: [I18n.t("activerecord.enums.#{klass.underscore}.#{enum}.#{key}", default: key.humanize), key] Кроме того, удалите последнее, humanizeпоскольку оно может исказить заглавные буквы, если в нем несколько слов
Питер П.
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

Если вы используете enum в 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»), и по какой-то причине мне нужно было заставить работать пустой параметр (с простой формой?):

В 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 %>
Грег Бласс
источник