Menyimpan enum dari pilih di Rails 4.1

108

Saya menggunakan enum di Rails 4.1 untuk melacak warna anggur.

Wine.rb

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

Dalam pandangan saya, saya menghasilkan pilihan sehingga pengguna dapat memilih anggur dengan warna tertentu

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

Ini menghasilkan HTML berikut:

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

Namun, setelah mengirimkan formulir, saya menerima kesalahan argumen yang menyatakan '1' is not a valid color. Saya menyadari ini karena colorharus sama 1dan tidak "1".

Apakah ada cara untuk memaksa Rails menafsirkan warna sebagai bilangan bulat daripada string?

Brian Weinreich
sumber

Jawaban:

203

Baiklah, jadi tampaknya, Anda tidak boleh mengirim nilai integer dari enum untuk disimpan. Anda harus mengirimkan nilai teks dari enum.

Saya mengubah masukan menjadi sebagai berikut:

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

Yang menghasilkan HTML berikut:

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

Nilainya berubah dari "0" menjadi "merah" dan sekarang kita sudah siap.


Jika Anda menggunakan text_field ol 'rails biasa itu:

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


Jika Anda ingin memiliki atribut bersih yang dapat dibaca manusia, Anda juga dapat melakukan:

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

Brian Weinreich
sumber
8
FWIW, saya perlu membuatnya f.select :color, Wine.colors.to_a.map { |w| [w[0].humanize, w[0]] }karena wmewakili sebuah array. Tidak yakin mengapa, tapi mungkin ini akan membantu seseorang.
jakealbaugh
menggunakan titleizemungkin ide yang lebih baik jika Anda memiliki dua kata atau lebih
Anwar
8
yang keysmetode mengembalikan array sehingga to_aberlebihan. Alternatifnya, metode pembantu pilih mengambil larik 2D untuk opsi sehingga Anda bisa menggunakannya to_a.
xander-miller
Bagaimana jika String Tidak Valid dikirim seperti "ERROR_JUNK_Submission". Jelas tidak ada nilai enum seperti ini, dan pengecualian akan diberikan. Dimana kita akan menangkapnya?
FlyingV
Model Wine memiliki enum yang disebut color enum color: [:red, :white, :sparkling]sehingga kalimat yang benar adalahf.input :color, :as => :select, :collection => Wine.color.keys.to_a
hguzman
32

Tidak perlu mengubah hash enum menjadi array to_a. Ini cukup:

f.select :color, Wine.colors.map { |key, value| [key.humanize, key] }
Sesama Orang Asing
sumber
10

Saya baru saja mengumpulkan EnumHelper yang menurut saya akan saya bagikan untuk membantu orang yang membutuhkan lebih banyak label dan lokal enum yang disesuaikan untuk pilihan enum Anda.

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

Di tempat Anda:

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

Dalam pandangan Anda:

 <%= f.select(:color, options_for_enum(@wine, :color)) %>
Andrew Cetinic
sumber
1
Pengeditan membuat kunci terjemahan lebih mudah dibaca dengan menemukan enum milik MultipleWordClassName di bawah multiple_word_class_name yang lebih umum daripada multiplewordclassname
Tetsujin no Oni
Bagaimana jika menambahkan default untuk fallback yang anggun: [I18n.t("activerecord.enums.#{klass.underscore}.#{enum}.#{key}", default: key.humanize), key] Juga, hapus yang terakhir humanizekarena dapat mengubah kapitalisasi jika beberapa kata
Peter
9

Solusi yang diterima tidak berhasil untuk saya karena dapat dibaca manusia , tetapi saya dapat membuatnya berfungsi seperti ini:

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

Ini adalah yang terbersih, tetapi saya benar-benar perlu memanusiakan kunci saya:

<%= f.select(:color, Wine.colors.keys) %>
Tom Rossi
sumber
1
rel 5 mendukung cara terbersih sekarang
V-SHY
@ V-SHY, dengan cara apa ini, dapatkah Anda menjelaskan?
ismailarilik
2
@ismailarilik, langsung berikan kunci sebagai opsi pilih, <% = f.select (: color, Wine.colors.keys)%>
V-SHY
6

Jika Anda menggunakan enum di Rails 4 maka panggil saja Model.enums:

f.select :color, Wine.colors.keys

Untuk membuat HTML:

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

Atau tambahkan metode di pengontrol:

def update_or_create
    change_enum_to_i
    ....
end

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

Inilah yang berhasil untuk saya, Rails 4+:

class Contract < ApplicationRecord

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


end

di my _form.html.erb, saya punya ini:

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

uji dari Konsol setelah menambahkan catatan:

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

Jika Anda perlu menangani i18n berdasarkan kunci enum, Anda dapat menggunakan:

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

dan dalam terjemahan Anda dapat mengatur warna:

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

Inilah solusi saya (peran saya memiliki garis bawah di dalamnya seperti "sales_rep"), dan untuk beberapa alasan inilah cara saya perlu mendapatkan opsi kosong untuk bekerja (dengan simpleform?):

Di 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

Kemudian dalam bentuk saya:

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