Использование поля продолжительности в модели Rails
Я ищу лучший способ использовать поле продолжительности в модели Rails. Я бы хотел, чтобы формат был HH: MM: SS (ex: 01:30:23). В используемой базе данных используется локальная база данных sqlite и Postgres.
Я также хотел бы работать с этим полем, чтобы я мог взглянуть на все объекты в поле и придумать общее время для всех объектов в этой модели и в итоге получить что-то вроде:
30 записей на 45 часов, 25 минут и 34 секунды.
Так что будет лучше всего работать?
- Тип поля для миграции
- Поле формы для форм CRUD (час, минута, вторая падающая вниз?)
- Самый дешевый способ генерации общей продолжительности всех записей в модели.
Ответы
Ответ 1
- Храните как целые числа в вашей базе данных (количество секунд, возможно).
- Ваша форма заявки будет зависеть от конкретного варианта использования. Выпадающие больны; лучше использовать небольшие текстовые поля для продолжительности в часах + минуты + секунды.
- Просто запустите запрос
SUM
по столбцу длительности, чтобы получить общую сумму. Если вы используете целые числа, это легко и быстро.
Дополнительно:
- Используйте помощник для отображения продолжительности в ваших представлениях. Вы можете легко преобразовать длительность как целое число секунд в
ActiveSupport::Duration
, используя 123.seconds
(замените 123
на целое число из базы данных). Используйте inspect
для полученного Duration
для хорошего форматирования. (Это не идеально. Вы можете написать что-то сами.)
- В вашей модели вам, вероятно, понадобятся читатели и авторы атрибутов, которые возвращают/принимают объекты
ActiveSupport::Duration
, а не целые числа. Просто определите duration=(new_duration)
и Duration
, которые внутренне вызывают read_attribute
/write_attribute
с целыми аргументами.
Ответ 2
Я попытался использовать ActiveSupport:: Duration, но не смог получить четкий вывод.
Вам может понравиться ruby-duration, неизменный тип, который представляет определенное количество времени с точностью в секундах. Он имеет множество тестов и тип поля модели Mongoid.
Я также хотел легко разбирать строки продолжительности жизни, поэтому я пошел с Chronic Duration. Вот пример добавления его в модель с полем time_spent в секундах.
class Completion < ActiveRecord::Base
belongs_to :task
belongs_to :user
def time_spent_text
ChronicDuration.output time_spent
end
def time_spent_text= text
self.time_spent = ChronicDuration.parse text
logger.debug "time_spent: '#{self.time_spent_text}' for text '#{text}'"
end
end
Ответ 3
В Rails 5 вы можете использовать ActiveRecord:: Attributes для хранения значений ActiveSupport:: Durations в качестве строк ISO8601. Преимущество использования ActiveSupport:: Duration над целыми числами заключается в том, что вы можете использовать их для расчета даты/времени прямо из коробки. Вы можете делать такие вещи, как Time.now + 1.month
, и это всегда правильно.
Вот как:
Добавить config/initializers/duration_type.rb
class DurationType < ActiveRecord::Type::String
def cast(value)
return value if value.blank? || value.is_a?(ActiveSupport::Duration)
ActiveSupport::Duration.parse(value)
end
def serialize(duration)
duration ? duration.iso8601 : nil
end
end
ActiveRecord::Type.register(:duration, DurationType)
Миграция
create_table :somethings do |t|
t.string :duration
end
Model
class Something < ApplicationRecord
attribute :duration, :duration
end
Использование
something = Something.new
something.duration = 1.year # 1 year
something.duration = nil
something.duration = "P2M3D" # 2 months, 3 days (ISO8601 string)
Time.now + something.duration # calculation is always correct
Ответ 4
Я написал несколько заглушек для поддержки и использования типа PostgreSQL interval
как ActiveRecord::Duration
.
См. этот gist (вы можете использовать его как инициализатор в Rails 4.1): https://gist.github.com/Envek/7077bfc36b17233f60ad
Кроме того, я открыл запросы на загрузку Rails:
https://github.com/rails/rails/pull/16919