В Rails, как мне реализовать поле «Статус» для приложения «Задачи» — целое число или перечисление?

Для приложения Todo на Rails 3.0 у меня есть модель Tasks с полем Status. Как лучше всего хранить данные поля «Статус» (тип поля) и по-прежнему отображать удобочитаемую версию в представлении (таблица HTML)? Статус может быть:

0 = нормальный
1 = активный
2 = завершенный

Прямо сейчас у меня есть это:

Схема рельсов здесь:

create_table "задачи", :force => true do |t|
t.integer "статус", :limit => 1, :default => 0, :null => false

Рельсовая модель здесь:

class Task < ActiveRecord::Base
  validates_inclusion_of :status, :in => 0..2,
    :message => "{{value}} must be 0, 1, or 2"

Рельсы Посмотреть здесь:

<h1>Listing tasks</h1>

<table>
  <tr>
    <th>Status</th>
    <th>Name</th>
    <th></th>
    <th></th>
    <th></th>
  </tr>

<% @tasks.each do |task| %>
  <tr>
    <td><%= task.status %></td>
    <td><%= task.name %></td>
    <td><%= link_to 'Show', task %></td>
    <td><%= link_to 'Edit', edit_task_path(task) %></td>
    <td><%= link_to 'Delete', task, :confirm => 'Are you sure?', :method => :delete %></td>
  </tr>
<% end %>
</table>

Требования

  1. Сохраните статус задачи в базе данных, чтобы значения можно было легко локализовать, т.е. я не уверен, что хочу хранить «нормальный», «активный», «завершенный» в виде строкового поля.

  2. Решение должно работать с Rails 3.0.

Вопросы:

  1. Должен ли я хранить поле как целое число (см. выше)? Если да, то как мне отобразить правильный удобочитаемый статус в таблице HTML в моем представлении Rails, например. показать «Активно» вместо «1» в таблице HTML.

  2. Должен ли я использовать перечисление? Если да, то легко ли это локализовать позже?

  3. Должен ли я использовать прямые строки, например. «Нормальный», «Активный», «Завершенный»

  4. Можете ли вы предоставить быстрый пример кода помощника представления, контроллера или кода представления, чтобы это работало?


person Doug    schedule 16.04.2010    source источник
comment
Вы спрашиваете довольно много в этом вопросе. Вы, вероятно, получите лучшие ответы, разделив его на несколько отдельных вопросов.   -  person Mike Buckbee    schedule 16.04.2010


Ответы (6)


1. Это зависит от того, насколько вы хотите оптимизировать запросы к БД.

2. Не совсем, не поддерживается "из коробки" AR. # Начиная с Rails 4 перечисления поддерживаются по умолчанию .

3. ИМХО, вы можете использовать строки без больших потерь производительности (только не забудьте добавить поле в индекс). Я бы сделал это, потому что его легче интернационализировать и поддерживать. Однако вы можете использовать целые числа, если вам нужна дополнительная производительность.

Вы можете взглянуть на 2 потока SO здесь и здесь, где это обсуждается.

4. Если вы хотите сохранить их целыми, вот как вы можете это сделать:

class Task << AR::Base
  NORMAL    = 1
  ACTIVE    = 2
  COMPLETED = 3


  STATUSES = {
    NORMAL    => 'normal',
    ACTIVE    => 'active',
    COMPLETED => 'completed'
  }

  validates_inclusion_of :status, :in => STATUSES.keys,
      :message => "{{value}} must be in #{STATUSES.values.join ','}"

  # just a helper method for the view
  def status_name
    STATUSES[status]
  end
end

и на виду:

<td><%= task.status_name %></td>

Если вы хотите использовать строки, это более просто:

STATUSES = ['normal', 'active', 'completed']
validates_inclusion_of :status, :in => STATUSES,
          :message => "{{value}} must be in #{STATUSES.join ','}"
person Vlad Zloteanu    schedule 16.04.2010
comment
Спасибо за подробный ответ. Вы убедили меня придерживаться строк для простоты. - person Doug; 16.04.2010

Проще всего было бы просто сохранить фактические строки в поле вместо добавления другой таблицы. В зависимости от вашей точки зрения это либо плохая идея, поскольку ваша база данных не будет достаточно нормализована, либо отличная идея, поскольку ваше приложение теперь более эффективно для нормализации.

Если вы решите этого не делать и сохраните значения в отдельной таблице; вам нужно настроить отношения в модели.

class Task < ActiveRecord::Base
    has_one :status
end

class Status < ActiveRecord::Base
    belongs_to :tasks
end 

Затем, по вашему мнению, вы можете ссылаться на значение:

<%= task.status %>
person Mike Buckbee    schedule 16.04.2010
comment
Это должно быть по-другому - person Abhinay; 20.03.2017

Я использовал Enum-Column для таких случаев использования. Плагин позволяет вам определить тип столбца перечисления в сценарии миграции и создает тип столбца перечисления MYSQL для атрибута.

create_table :tasks do |t|
  ...
  t.enum :status, :limit => [:normal, :active, :completed], :default => :normal
  ...
end

Теперь в вашем коде вы можете сделать следующее:

task.status = "active"
task.status = :completed
p "Task status: #{task.status}" # prints Task status: completed


Task.find_by_status(:active)
Task.find_by_status("active")
Task.find_by_status(2)

Хорошо работает и с сериализацией:

task.to_xml # will result in status= active instead of status-2

Другим приятным аспектом является то, что значения состояния отображаются в виде строк при просмотре с помощью обычного клиента БД (например, командной консоли mysql или phpMyAdmin).

Плагин обеспечивает оптимальное хранение и удобный доступ для типов перечисления.

Предостережение:

Плагин довольно старый и не поддерживается. Я широко использую его с MySQL DB. Мне пришлось исправить код, чтобы он работал на PostgreSQL. Если вы используете MySQL, этот плагин — хороший выбор.

person Harish Shetty    schedule 16.04.2010

Я предпочитаю хранить «нормальный», «активный», .. «завершенный» в виде строки в таблице, потому что:

  • это самостоятельная документация (например, кто-то может смотреть на данные, но никогда не читать исходный код Rails)
  • есть небольшое (если не полное) снижение производительности
  • (все еще) легко сделать i18n с помощью виртуального атрибута Rails в модели или любого другого слоя на других языках.

В настоящее время я стремлюсь максимально отделить константы Rails от базы данных. Вокруг нас всегда есть люди, работающие с PHP/MSSQL/??DBA (которые могут не любить Rails так сильно, как мы ;-)

Итак, ответ не целочисленный и не enum (а varchar ;-)

person ohho    schedule 16.04.2010
comment
Я пожалел об этом решении, когда нам нужно было переименовать несколько статусов по деловым причинам. 10 миллионов записей в производстве для обновления против 1 строки кода, если бы я использовал перечисления или маршрут, предложенный в принятом ответе - person konung; 04.10.2014

Я знаю, что это старый вопрос, но я хотел упомянуть 2 момента, которые исходят из опыта, особенно если кто-то ищет это сейчас (2014 г. - OQ был в 2010 г.):

  1. Если вы начинаете новый проект > Rails 4 (технически ActiveRecord 4) — используйте Enums — наиболее эффективный маршрут. Особенно, если в дальнейшем вам потребуется создавать какие-либо сложные SQL-запросы.
  2. Есть и другая альтернатива: создайте составную модель статуса, которая будет содержать статусы для всех других ваших моделей. Сделайте это моделью STI (добавьте столбец типа) — тогда вы сможете создавать такие вещи, как OrderStatus ‹ Status или CustomerStatus ‹ Status, и ваш заказ и клиент будут иметь атрибуты status_id. Это приводит к более медленным (!) и более громоздким (!) запросам, однако вы можете пойти по этому пути, если вы создаете приложение, которое будет отправлено клиенту, у которого нет технических знаний, и им потребуется какая-то способность добавлять/удалять статусы через что-то вроде ActiveAdmin самостоятельно, а не модифицировать свою кодовую базу. Это также вариант, если ваш уровень данных не может обрабатывать перечисления.
person konung    schedule 03.10.2014
comment
Перечисления в Rails действительно сложны и запутаны. См. эту статью для некоторых ошибок: hackhands.com/ruby- on-enums-queries-and-rails-4-1 - person AlexChaffee; 30.04.2015

Использование целого числа для хранения статуса — хорошая идея. Тем не менее, я думаю, что код, предоставленный принятым ответом, не является элегантным, поскольку он загрязняет весь класс модели только из-за атрибута.

Мое предложение переопределяет метод получения атрибута:

def status
  {
    0 => "active",
    1 => "inactive"
  }[read_attribute(:status)] # use the read_attribute method to prevent infinite loop.
end

Логика преобразования целого числа в строку будет только в этом методе-геттере, так что загрязнять класс не нужно.

person Brian    schedule 26.11.2014