Лучший способ сохранить значение Enum в ActiveRecord и преобразовать в строку для отображения
Я пытаюсь выяснить, что является лучшим способом сохранить значение enum в activerecord, но преобразовать его в "заголовок" для отображения в приложении.
т.е.
Review Enum:
UNREVIEWED = {:title => "Unreviewed", :name => "UNREVIEWED"}
REVIEWED = {:title => "Reviewed", :name => "REVIEWED"}
FLAGGED = {:title => "Flagged as inappropriate", :name => "FLAGGED"}
Итак, в java-стране я использовал для хранения имени ENUM, т.е. (REVIEWED) в базе данных, а затем преобразовал это имя в это фактическое перечисление на сервере, чтобы я мог называть его вспомогательные методы, то есть:
review = Review.valueOf(review)
review.title()
Есть ли что-то подобное в rails для выполнения этого?
FYI мы стараемся, чтобы наше приложение было очень маленьким, поэтому, если я могу легко выполнить это или что-то подобное без GEM, это было бы здорово.
Любой "стандартный" способ сделать это, поскольку я полагаю, что я не первый, кто борется с этой проблемой?
Спасибо!
Ответы
Ответ 1
Перечисления ActiveRecord - лучший способ, поскольку он является частью фреймворка (начиная с версии 4.1).
Его использование довольно просто:
Миграция
class AddEnumToMyModel < ActiveRecord::Migration
def change
add_column :my_model, :status, :integer, default: 0
end
end
Модель:
class MyModel < ActiveRecord::Base
enum status: [:draft, :beta, :public]
end
Затем используйте его волю:
MyModel.draft # gets all drafts
MyModel.last.draft? # checks if the last model is draft
MyModel.last.status # gets the string description of the status of my model
Информацию о режиме см. в документации.
Ответ 2
есть много сообщений об этой проблеме, я думаю, это указывает на большинство из них: http://thinkinginrails.com/2010/04/using-enums-for-constants/
Я думаю, что это слишком сложная вещь, которая вам не нужна в динамически типизированном языке, таком как ruby.
просто используйте строки!
вы могли бы использовать это как:
class Review < ActiveRecord::Base
validates_inclusion_of :status, :in => ["UNREVIEWED", "REVIEWED", "FLAGGED"]
def status
read_attribute(:status)
end
def status= (value)
write_attribute(:status, value)
end
end
Ответ 3
Использование примера @Godsaur.
class MyModel < ActiveRecord::Base
enum status: [:draft, :beta, :public]
end
Вы можете получить строковое значение как:
MyModel.last.status
=> "beta"
Но если вы хотите "заголовок", вы можете:
MyModel.last.status.titleize
=> "Beta"
Ответ 4
Хотя я согласен с тем, что ответ @Godsaur верен - мне лично не нравится подход к хранению целочисленных значений для представления значимых эквивалентов строк (при условии, что выполняется достаточная индексация базы данных и/или стоимость запросов строк в модуле БД аналогичен запросу целых чисел).
Мой подход обычно хранит текстовые значения в базе данных (для более простого понимания записей БД).
my_model.rb
class MyModel < ActiveRecord::Base
STATUSES = HashWithIndifferentAccess.new({unverified: 'unverified', reviewed: 'reviewed', flagged: 'flagged'})
#the text values are similar to their symbols key
#you could also define additional attributes for each key if needed. Ex:
#STATUSES = {unverified: {title: 'Unverified Text Title', txt: 'unverified'}, ...}
# assuming a 'status' field
scope :unverified, -> { where(status: STATUSES[:unverified]) }
def unverified?
status == STATUSES[:unverified]
end
# Or
STATUSES.each do |sym, val|
define_method("#{sym}?") {status == val}
end
end
my_view.erb
<%= MyModel::STATUSES[@my_model.status] %> or
<%= MyModel::STATUSES[@my_model.status].title %>
Ответ 5
Я согласен с @tamersalama. Здесь простой способ сделать это, используя строки в БД с пользовательским классом Enum. Обратите внимание, что это также поддерживает читаемые пользователем имена для использования в выпадающих меню.
https://gist.github.com/alexch/a7be54e1b085718473ff
SQL:
Table name: snacks
id :integer
ice_cream :string
Рельсы:
class Snack < ActiveRecord::Base
FLAVORS = Enum.new [
[:vanilla, "Vanilla"],
[:chocolate, "Chocolate"],
[:rocky_road, "Rocky Road"]
])
]
end
HTML:
<%= simple_form_for @snack do |f| %>
<%= f.collection_select :ice_cream, Snack::FLAVORS, :value, :label %>
<% end %>