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?

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


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

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

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

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

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"

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

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 %>
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.