ActiveAdmin and Formtastic: Displaying value for radio buttons and field selection using ENUM

I am using ActiveAdmin to manage a large database and one of my models (ItemType) has an ENUM attribute (ItemType.units) and I can use Formtastic to render a select box and radio buttons like:

f.input :unit, :as => :radio, :collection => ItemType.units, include_blank: false

      

The ENUM field is defined in the model as follows:

class ItemType < ActiveRecord::Base
  enum unit: [ :Packages, :Pieces, :KG ]
end

      

This works correctly when creating a new resource, but the value is not retrieved when using a form to edit the same resource.


This uses the default "special view" for the database record:

The singular view

And here, by default, a "preview" is displayed for the same entry. Note that none of the values ​​are selected:

The edit view

+3


source to share


2 answers


When you declare an enum and then access the display, it returns a hash:

ItemType.units #=> { "Packages" => 0, "Pieces" => 1, "KG" => 2 }

      

When you use it as a collection for your radio inputs, it sets the inputs to 0, 1, 2, etc. This value does not match the return value ItemType#unit

(since it will return a string representation of an enumeration such as "KG"

).

Rails can only set the selected value when one of the values ​​in the list matches the value of the attribute. This will never happen in this case.

This duality (string vs integer) will lead to another point of pain. You cannot actually save the form because you can only set the enum value to one of the allowed values ​​(string or integer representation).

it = ItemType.new
it.unit = "KG" # this works
it.unit = :KG  # this works as well
it.unit = 1    # this works but WTF?!
it.unit = "1"  # this will raise an ArgumentError

      

Since the form parameters are parsed into a string, ActiveAdmin will try to assign "1"

to ItemType#unit

and it will fail.



The solution is actually pretty simple. Use only keys from display:

f.input :unit, :as => :radio, :collection => ItemType.units.keys

      

Although, if you can, you should stay away from using AR enums. A few reasons:

  • it is a meaningful string with a meaningless number (this means that the data in the one column won't make any sense without application code)
  • it tightly connects the application source to the data (it would be difficult to use the data separately in another application like the db console).
  • the order of the enumeration values ​​must be maintained (or an explicit mapping must be provided). None of them support developers.

Your best bet is to use a predefined array of strings and check if the value is in the list of predefined values. Something like lines:

class ItemType
  UNITS = %w[kg packages pieces]
  validates :unit, inclusion: { in: UNITS }
end

      

+4


source


I would use constants instead of enum:



class ItemType < ActiveRecord::Base
  UNIT_VALUES = %w{Packages Pieces KG}
end

f.input :units, :as => :radio, :collection => ItemType::UNIT_VALUES, include_blank: false

      

0


source







All Articles