Rails 5和PostgreSQL'Interval'数据类型

时间:2017-05-11 16:13:53

标签: ruby-on-rails postgresql activerecord

Rails是否真的不能正确支持PostgreSQL的间隔数据类型?

我不得不使用2013年的this Stack Overflow answer来创建一个间隔列,现在看来我需要使用2013年的this piece of code来获取ActiveRecord来将间隔视为除了字符串。

那是怎么回事?我最好只使用整数数据类型代表分钟数吗?

4 个答案:

答案 0 :(得分:3)

在Rails 5.1中,您可以使用postgres的“间隔”数据类型,因此可以在迁移中执行以下操作:

add_column :your_table, :new_column, :interval, default: "2 weeks"

尽管ActiveRecord仅将间隔视为字符串,但是如果在Postgresql数据库中将IntervalStyle设置为iso_8601,它将以iso8601样式显示间隔:2 weeks => P14D

execute "ALTER DATABASE your_database SET IntervalStyle = 'iso_8601'"

然后您可以直接将列解析为ActiveSupport::Duration

在您的model.rb

def new_column
  ActiveSupport::Duration.parse self[:new_column]
end

有关ISO8601间隔的更多信息,请访问https://en.wikipedia.org/wiki/ISO_8601#Time_intervals

答案 1 :(得分:1)

我遇到了类似的问题,并为ActiveRecord模型上的特定列定义了阅读器方法。像这样:

class DivingResults < ActiveRecord::Base
  # This overrides the same method for db column, generated by rails
  def underwater_duration
    interval_from_db = super
    time_parts = interval_from_db.split(':')
    if time_parts.size > 1 # Handle formats like 17:04:41.478432
      units = %i(hours minutes seconds)
      in_seconds = time_parts
        .map.with_index { |t,i| t.to_i.public_send(units[i]) }
        .reduce(&:+) # Turn each part to seconds and then sum
      ActiveSupport::Duration.build in_seconds
    else # Handle formats in seconds
      ActiveSupport::Duration.build(interval_from_db.to_i)
    end
  end
end

这允许在其他地方使用ActiveSupport::Duration实例。希望Rails将在不久的将来自动开始处理PostgreSQL间隔数据类型。

答案 2 :(得分:1)

答案 3 :(得分:0)

类似于Madis的解决方案,该解决方案可以处理几分之一秒的时间和ISO8601持续时间:

def duration
  return nil unless (value = super)

  # Handle ISO8601 duration
  return ActiveSupport::Duration.parse(value) if value.start_with?('P')

  time_parts = value.split(':')
  if time_parts.size > 1
    # Handle formats like 17:04:41.478432
    units = %i[hours minutes seconds]
    in_seconds = time_parts.map.with_index { |t, i| t.to_f.public_send(units[i]) }.reduce(&:+)
    ActiveSupport::Duration.build in_seconds
  else
    # Handle formats in seconds
    ActiveSupport::Duration.build(value)
  end
end

def duration=(value)
  unless value.is_a?(String)
    value = ActiveSupport::Duration.build(value).iso8601
  end

  self[:duration] = value
end

这假设您像Leo在他的回答中提到的那样设置数据库。不知道为什么有时它们以PT42S格式有时以00:00:42.000格式从Postgres回来:/