Enregistrement de l'énumération de select dans Rails 4.1


108

J'utilise les enums dans Rails 4.1 pour garder une trace des couleurs du vin.

Wine.rb

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

À mon avis, je génère une sélection pour que l'utilisateur puisse sélectionner un vin avec une certaine couleur

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

Cela génère le HTML suivant:

<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>

Cependant, lors de la soumission du formulaire, je reçois une erreur d'argument indiquant '1' is not a valid color. Je me rends compte que c'est parce qu'il colorfaut égaler 1et non "1".

Existe-t-il un moyen de forcer Rails à interpréter la couleur comme un entier plutôt que comme une chaîne?

Réponses:


203

D'accord, donc apparemment, vous ne devriez pas envoyer la valeur entière de l'énumération à enregistrer. Vous devez envoyer la valeur de texte de l'énumération.

J'ai changé l'entrée pour être la suivante:

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

Qui a généré le HTML suivant:

<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>

Les valeurs sont passées de "0" à "rouge" et maintenant nous sommes tous ensemble.


Si vous utilisez un ancien text_field de rails classiques, c'est:

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


Si vous voulez avoir des attributs lisibles par l'homme, vous pouvez également faire:

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


8
FWIW, j'avais besoin de le faire f.select :color, Wine.colors.to_a.map { |w| [w[0].humanize, w[0]] }car wreprésentait un tableau. Je ne sais pas pourquoi, mais peut-être que cela aidera quelqu'un.
jakealbaugh

l'utilisation titleizepourrait être une meilleure idée si vous avez deux mots ou plus
Anwar

8
la keysméthode renvoie un tableau donc le to_aest redondant. Alternativement, la méthode d'aide à la sélection prend un tableau 2D pour les options que vous pouvez simplement utiliser to_a.
xander-miller

Que faire si une chaîne non valide est envoyée telle que "ERROR_JUNK_Submission". De toute évidence, il n'y a pas de valeur d'énumération comme celle-ci et une exception est levée. Où pourrions-nous l'attraper?
FlyingV

Le modèle Wine a une énumération appelée couleur, enum color: [:red, :white, :sparkling]donc la phrase correcte estf.input :color, :as => :select, :collection => Wine.color.keys.to_a
hguzman

32

Pas besoin de convertir le hachage enum en tableau avec to_a. Cela suffit:

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

10

Je viens de créer un EnumHelper que je pensais partager pour aider les personnes qui ont besoin d'étiquettes d'énumération et de paramètres régionaux plus personnalisés pour vos sélections d'énumération.

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

Dans votre région:

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

À votre avis:

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

1
La modification rend les clés de traduction plus lisibles en trouvant des énumérations appartenant à MultipleWordClassName sous le nom_classe_mots_multiple plutôt plus typique que le
nomclasse_mots

Que diriez-vous d'ajouter une valeur par défaut pour un remplacement gracieux: [I18n.t("activerecord.enums.#{klass.underscore}.#{enum}.#{key}", default: key.humanize), key] supprimez également le dernier humanizecar il peut déformer la capitalisation si ses mots multiples
Peter P.

9

La solution acceptée n'a pas fonctionné pour moi pour la lecture humaine , mais j'ai pu la faire fonctionner comme ceci:

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

C'était le plus propre, mais j'avais vraiment besoin d'humaniser mes clés:

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

1
rails 5 prend en charge la manière la plus propre maintenant
V-SHY

@ V-SHY, qu'est-ce que c'est que cette façon, pouvez-vous expliquer?
ismailarilik

2
@ismailarilik, fournissez directement les clés comme option de sélection, <% = f.select (: color, Wine.colors.keys)%>
V-SHY

6

Si vous utilisez enum dans Rails 4, appelez simplement Model.enums:

f.select :color, Wine.colors.keys

Pour créer du HTML:

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

Ou ajoutez une méthode dans le contrôleur:

def update_or_create
    change_enum_to_i
    ....
end

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

4

Voici ce qui a fonctionné pour moi, Rails 4+:

class Contract < ApplicationRecord

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


end

dans my _form.html.erb, j'ai ceci:

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

test depuis la console après l'ajout d'un enregistrement:

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

Si vous devez gérer le i18n en fonction des clés d'énumération, vous pouvez utiliser:

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

et dans les traductions, vous pouvez définir les couleurs:

wine:
 red: Red
 white: White

1

Voici ma solution (mes rôles ont des traits de soulignement comme "sales_rep"), et pour une raison quelconque, c'était comme ça que j'avais besoin d'obtenir une option vide pour fonctionner (avec simpleform?):

Dans 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

Puis sous ma forme:

<%= f.input :role, collection: enum_collection_for_select(User.roles), selected: @user.role %>
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.