Salvare enum da select in Rails 4.1


108

Sto usando le enumerazioni in Rails 4.1 per tenere traccia dei colori del vino.

Wine.rb

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

A mio avviso, creo una selezione in modo che l'utente possa selezionare un vino con un certo colore

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

Questo genera il seguente codice 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>

Tuttavia, dopo aver inviato il modulo, ricevo un messaggio di errore di argomento '1' is not a valid color. Mi rendo conto che questo è perché colordeve essere uguale 1e non "1".

C'è un modo per forzare Rails a interpretare il colore come un intero invece che come una stringa?

Risposte:


203

Va bene, quindi a quanto pare, non dovresti inviare il valore intero dell'enumerazione da salvare. Dovresti inviare il valore di testo dell'enumerazione.

Ho cambiato l'input in modo che fosse il seguente:

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

Che ha generato il seguente codice 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>

I valori sono passati da "0" a "rosso" e ora siamo a posto.


Se stai usando un vecchio campo di testo rails è:

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


Se vuoi avere attributi leggibili dall'uomo, puoi anche fare:

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


8
FWIW, avevo bisogno di farlo f.select :color, Wine.colors.to_a.map { |w| [w[0].humanize, w[0]] }perché wrappresentava un array. Non sono sicuro del perché, ma forse questo aiuterà qualcuno.
jakealbaugh

usare titleizepotrebbe essere un'idea migliore se hai due o più parole
Anwar

8
il keysmetodo restituisce un array quindi to_aè ridondante. In alternativa, il metodo di selezione dell'helper accetta un array 2D per le opzioni, quindi puoi semplicemente usarlo to_a.
xander-miller

Cosa succede se viene inviata una stringa non valida come "ERROR_JUNK_Submission". Ovviamente non esiste un valore enum come questo e viene generata un'eccezione. Dove lo prenderemmo?
FlyingV

Il modello Wine ha un'enumerazione chiamata color, enum color: [:red, :white, :sparkling]quindi la frase corretta èf.input :color, :as => :select, :collection => Wine.color.keys.to_a
hguzman

32

Non è necessario convertire l'hash enum in array con to_a. Questo è sufficiente:

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

10

Ho appena messo insieme un EnumHelper che ho pensato di condividere per aiutare le persone che hanno bisogno di etichette e localizzazioni enum più personalizzate per le tue selezioni enum.

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

Nella tua località:

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

Nelle tue opinioni:

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

1
La modifica rende le chiavi di traduzione più leggibili trovando enumerazioni appartenenti a MultipleWordClassName sotto il più tipico multiple_word_class_name piuttosto che multiplewordclassname
Tetsujin no Oni

Che ne dici di aggiungere un valore predefinito per un grazioso fallback: [I18n.t("activerecord.enums.#{klass.underscore}.#{enum}.#{key}", default: key.humanize), key] Inoltre, rimuovi l'ultimo humanizein quanto potrebbe distorcere le maiuscole se le sue più parole
Peter P.

9

La soluzione accettata non ha funzionato per me per il leggibile umano , ma sono riuscito a farlo funzionare in questo modo:

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

Questo era il più pulito, ma avevo davvero bisogno di umanizzare le mie chiavi:

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

1
rails 5 ora supporta il modo più pulito
V-SHY

@ V-SHY, cos'è questo modo, puoi spiegare?
ismailarilik

2
@ismailarilik, fornisci direttamente le chiavi come opzione selezionata, <% = f.select (: color, Wine.colors.keys)%>
V-SHY

6

Se usi enum in Rails 4, chiama semplicemente Model.enums:

f.select :color, Wine.colors.keys

Per creare HTML:

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

Oppure aggiungi il metodo nel controller:

def update_or_create
    change_enum_to_i
    ....
end

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

4

Ecco cosa ha funzionato per me, Rails 4+:

class Contract < ApplicationRecord

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


end

in my _form.html.erb, ho questo:

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

test dalla console dopo aver aggiunto un record:

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

Se devi gestire i18n in base alle chiavi enum puoi usare:

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

e nelle traduzioni puoi impostare i colori:

wine:
 red: Red
 white: White

1

Ecco la mia soluzione (i miei ruoli hanno trattini bassi come "sales_rep"), e per qualche motivo questo era il motivo per cui avevo bisogno di far funzionare un'opzione vuota (con simpleform?):

In 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

Quindi nella mia forma:

<%= f.input :role, collection: enum_collection_for_select(User.roles), selected: @user.role %>
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.