【问题标题】:Using a duration field in a Rails model在 Rails 模型中使用持续时间字段
【发布时间】:2010-11-06 07:01:19
【问题描述】:

我正在寻找在 Rails 模型中使用持续时间字段的最佳方式。我希望格式为 HH:MM:SS(例如:01:30:23)。使用的数据库是本地的 sqlite 和生产中的 Postgres。

我也想使用这个字段,这样我就可以查看该字段中的所有对象,并得出该模型中所有对象的总时间,并最终得到如下结果:

30 条记录,总计 45 小时 25 分 34 秒。

那么什么最适合呢?

  • 迁移的字段类型
  • CRUD 表单的表单域(小时、分钟、秒下拉菜单?)
  • 生成模型中所有记录的总持续时间的成本最低的方法

【问题讨论】:

    标签: ruby-on-rails ruby time duration


    【解决方案1】:
    • 在数据库中存储为整数(可能是秒数)。
    • 您的输入表单将取决于具体的用例。下拉菜单很痛苦;最好在小时 + 分钟 + 秒内使用小文本字段。
    • 只需对持续时间列运行SUM 查询即可生成总计。如果您使用整数,这将简单快捷。

    另外:

    • 使用帮助程序在视图中显示持续时间。您可以使用123.seconds(将123 替换为数据库中的整数)轻松地将持续时间(以秒为整数)转换为ActiveSupport::Duration。在生成的Duration 上使用inspect 以获得良好的格式。 (它并不完美。您可能想自己写一些东西。)
    • 在您的模型中,您可能需要返回/获取ActiveSupport::Duration 对象而不是整数的属性读取器和写入器。只需定义 duration=(new_duration)duration,它们在内部使用整数参数调用 read_attribute / write_attribute

    【讨论】:

    • 我从来没有考虑过使用秒,呵呵!感谢您的提示!
    • 如何使用inspect 来格式化输出?例子?
    【解决方案2】:

    在 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
    

    型号

    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
    

    【讨论】:

      【解决方案3】:

      我尝试使用 ActiveSupport::Duration,但无法让输出清晰。

      您可能会喜欢ruby-duration,这是一种不可变类型,它以秒为单位表示一定量的时间。它有很多测试和一个 Mongoid 模型字段类型。

      我还想轻松解析人类持续时间字符串,所以我选择了Chronic Duration。这是一个将其添加到具有 time_spent in seconds 字段的模型的示例。

      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
      

      【讨论】:

        【解决方案4】:

        我编写了一些存根来支持和使用 PostgreSQL 的 interval 类型作为 ActiveRecord::Duration

        请参阅此要点(您可以将其用作 Rails 4.1 中的初始化程序):https://gist.github.com/Envek/7077bfc36b17233f60ad

        我还在那里打开了对 Rails 的拉取请求: https://github.com/rails/rails/pull/16919

        【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-01-19
        • 1970-01-01
        • 2019-06-29
        • 1970-01-01
        • 1970-01-01
        • 2013-07-28
        • 2011-07-24
        • 1970-01-01
        相关资源
        最近更新 更多