Guardar enumeración de seleccionar en Rails 4.1

108

Estoy usando las enumeraciones en Rails 4.1 para realizar un seguimiento de los colores del vino.

Wine.rb

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

En mi opinión, genero una selección para que el usuario pueda seleccionar un vino con un color determinado.

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

Esto genera el siguiente 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>

Sin embargo, al enviar el formulario, recibo un error de argumento que indica '1' is not a valid color. Me doy cuenta de que esto se debe a que colordebe ser igual 1y no "1".

¿Hay alguna forma de obligar a Rails a interpretar el color como un número entero en lugar de una cadena?

Brian Weinreich
fuente

Respuestas:

203

Muy bien, aparentemente, no debería enviar el valor entero de la enumeración para guardarlo. Debe enviar el valor de texto de la enumeración.

Cambié la entrada para que sea la siguiente:

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

Que generó el siguiente 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>

Los valores pasaron de "0" a "rojo" y ahora estamos listos.


Si está utilizando un campo de texto de rieles tradicionales, es:

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


Si desea tener atributos limpios legibles por humanos, también puede hacer:

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

Brian Weinreich
fuente
8
FWIW, necesitaba hacerlo f.select :color, Wine.colors.to_a.map { |w| [w[0].humanize, w[0]] }porque wrepresentaba una matriz. No estoy seguro de por qué, pero tal vez esto ayude a alguien.
jakealbaugh
usar titleizepodría ser una mejor idea si tiene dos o más palabras
Anwar
8
el keysmétodo devuelve una matriz, por lo que to_aes redundante. Alternativamente, el método de ayuda de selección toma una matriz 2D para opciones para que pueda usar to_a.
xander-miller
¿Qué sucede si se envía una cadena no válida como "ERROR_JUNK_Submission"? Obviamente, no hay un valor de enumeración como este, y se lanza una excepción. ¿Dónde lo atraparíamos?
FlyingV
El modelo Wine tiene una enumeración llamada color, enum color: [:red, :white, :sparkling]por lo que la oración correcta esf.input :color, :as => :select, :collection => Wine.color.keys.to_a
hguzman
32

No es necesario convertir el hash de enumeración en una matriz con to_a. Esto es suficiente:

f.select :color, Wine.colors.map { |key, value| [key.humanize, key] }
Compañero extraño
fuente
10

Acabo de armar un EnumHelper que pensé en compartir para ayudar a las personas que necesitan etiquetas de enumeración y configuraciones regionales más personalizadas para sus selecciones de enumeración.

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 su localidad:

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

En sus opiniones:

 <%= f.select(:color, options_for_enum(@wine, :color)) %>
Andrew Cetinic
fuente
1
La edición hace que las claves de traducción sean más legibles al encontrar enumeraciones que pertenecen a MultipleWordClassName bajo el bastante más típico multiple_word_class_name en lugar de multiplewordclassname
Tetsujin no Oni
¿Qué tal si agregas un valor predeterminado para una alternativa elegante? [I18n.t("activerecord.enums.#{klass.underscore}.#{enum}.#{key}", default: key.humanize), key] Además, elimina el último, humanizeya que puede distorsionar las mayúsculas si son varias palabras
Peter P.
9

La solución aceptada no funcionó para mí para la lectura humana , pero pude hacer que funcionara así:

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

Este fue el más limpio, pero realmente necesitaba humanizar mis llaves:

<%= f.select(:color, Wine.colors.keys) %>
Tom Rossi
fuente
1
rails 5 soporta la forma más limpia ahora
V-SHY
@ V-SHY, ¿qué es esta forma, me lo puedes explicar?
ismailarilik
2
@ismailarilik, proporciona directamente las teclas como opción de selección, <% = f.select (: color, Wine.colors.keys)%>
V-SHY
6

Si usa enum en Rails 4, simplemente llame Model.enums:

f.select :color, Wine.colors.keys

Para crear HTML:

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

O agregue el método en el controlador:

def update_or_create
    change_enum_to_i
    ....
end

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

Esto es lo que funcionó para mí, Rails 4+:

class Contract < ApplicationRecord

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


end

en my _form.html.erb, tengo esto:

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

prueba desde la consola después de agregar un registro:

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

Si necesita manejar el i18n en función de las claves de enumeración, puede usar:

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

y en las traducciones puedes configurar los colores:

wine:
 red: Red
 white: White
Paulo Fidalgo
fuente
1

Aquí está mi solución (mis roles tienen guiones bajos como "sales_rep"), y por alguna razón, así era como necesitaba obtener una opción en blanco para trabajar (¿con simpleform?):

En 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

Entonces en mi forma:

<%= f.input :role, collection: enum_collection_for_select(User.roles), selected: @user.role %>
Greg Blass
fuente