Zapisywanie wyliczenia z select w Railsach 4.1

108

Używam wyliczeń w Railsach 4.1 do śledzenia kolorów wina.

Wine.rb

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

Moim zdaniem generuję selekcję, aby użytkownik mógł wybrać wino o określonym kolorze

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

Spowoduje to wygenerowanie następującego kodu 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>

Jednak po przesłaniu formularza otrzymuję błąd w argumentacji '1' is not a valid color. Zdaję sobie sprawę, że to dlatego, że colormusi równać się 1i nie "1".

Czy istnieje sposób, aby zmusić Railsy do interpretowania koloru jako liczby całkowitej zamiast łańcucha?

Brian Weinreich
źródło

Odpowiedzi:

203

W porządku, więc najwyraźniej nie powinieneś wysyłać wartości całkowitej wyliczenia do zapisania. Należy przesłać wartość tekstową wyliczenia.

Zmieniłem dane wejściowe na następujące:

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

Który wygenerował następujący kod 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>

Wartości zmieniły się z „0” na „czerwony” i teraz wszystko jest ustawione.


Jeśli używasz zwykłych starych railsów text_field, jest to:

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


Jeśli chcesz mieć czyste atrybuty czytelne dla człowieka, możesz również zrobić:

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

Brian Weinreich
źródło
8
FWIW, musiałem to zrobić, f.select :color, Wine.colors.to_a.map { |w| [w[0].humanize, w[0]] }ponieważ wreprezentował tablicę. Nie wiem dlaczego, ale może to komuś pomoże.
jakealbaugh
używanie titleizemoże być lepszym pomysłem, jeśli masz dwa lub więcej słów
Anwar
8
keyszwraca tablicę więc to_ajest zbędne. Alternatywnie, metoda pomocnika select pobiera tablicę 2D dla opcji, więc możesz po prostu użyć to_a.
xander-miller
Co się stanie, jeśli zostanie wysłany nieprawidłowy ciąg, na przykład „ERROR_JUNK_Submission”. Oczywiście nie ma takiej wartości wyliczenia i zgłaszany jest wyjątek. Gdzie byśmy to złapali?
FlyingV
Model Wine ma wyliczenie o nazwie color, enum color: [:red, :white, :sparkling]więc poprawne zdanie tof.input :color, :as => :select, :collection => Wine.color.keys.to_a
hguzman
32

Nie ma potrzeby konwertowania wartości skrótu wyliczenia na tablicę z to_a. To wystarczy:

f.select :color, Wine.colors.map { |key, value| [key.humanize, key] }
Kolega nieznajomy
źródło
10

Właśnie stworzyłem EnumHelper, który pomyślałem, że mógłbym udostępnić, aby pomóc ludziom, którzy potrzebują bardziej spersonalizowanych etykiet wyliczeniowych i lokalizacji dla wybranych wyliczeń.

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

W Twoim języku:

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

W Twoich poglądach:

 <%= f.select(:color, options_for_enum(@wine, :color)) %>
Andrew Cetinic
źródło
1
Edycja sprawia, że ​​klucze tłumaczenia są bardziej czytelne, znajdując wyliczenia należące do MultipleWordClassName pod bardziej typową nazwą wielu_słów_klasy_klasy zamiast wielu
nazwklasy słów
Co powiesz na dodanie wartości domyślnej dla eleganckiego rozwiązania zastępczego: [I18n.t("activerecord.enums.#{klass.underscore}.#{enum}.#{key}", default: key.humanize), key] usuń również ostatnią, humanizeponieważ może zniekształcać wielkie litery, jeśli zawiera wiele słów
Peter P.
9

Zaakceptowane rozwiązanie nie działało dla mnie w przypadku czytelnej dla człowieka , ale udało mi się sprawić, aby działało w następujący sposób:

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

To był najczystszy, ale naprawdę musiałem uczłowieczyć moje klucze:

<%= f.select(:color, Wine.colors.keys) %>
Tom Rossi
źródło
1
rails 5 obsługuje teraz najczystszy sposób
V-SHY
@ V-SHY, co to jest, czy możesz wyjaśnić?
ismailarilik
2
@ismailarilik, podaj bezpośrednio klucze jako opcję wyboru, <% = f.select (: color, Wine.colors.keys)%>
V-SHY
6

Jeśli używasz wyliczenia w Railsach 4, po prostu zadzwoń Model.enums:

f.select :color, Wine.colors.keys

Aby stworzyć HTML:

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

Lub dodaj metodę w kontrolerze:

def update_or_create
    change_enum_to_i
    ....
end

def change_enum_to_i
    params[:f]["color"] = params[:f]["color"].to_i
end
ogelacinyc
źródło
4

Oto co zadziałało w moim przypadku, Rails 4+:

class Contract < ApplicationRecord

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


end

w my _form.html.erb, mam to:

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

test z konsoli po dodaniu rekordu:

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
źródło
3

Jeśli potrzebujesz obsługiwać i18n w oparciu o klucze wyliczeniowe, możesz użyć:

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

aw tłumaczeniach można ustawić kolory:

wine:
 red: Red
 white: White
Paulo Fidalgo
źródło
1

Oto moje rozwiązanie (moje role mają podkreślenia, takie jak „sales_rep”) iz jakiegoś powodu potrzebowałem pustej opcji do pracy (z simpleform?):

W 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

Następnie w mojej formie:

<%= f.input :role, collection: enum_collection_for_select(User.roles), selected: @user.role %>
Greg Blass
źródło