Rails是否真的不能正确支持PostgreSQL的间隔数据类型?
我不得不使用2013年的this Stack Overflow answer来创建一个间隔列,现在看来我需要使用2013年的this piece of code来获取ActiveRecord来将间隔视为除了字符串。
那是怎么回事?我最好只使用整数数据类型代表分钟数吗?
答案 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)
A more complete and integrated solution is available in Rails 6.1
当前的答案建议覆盖模型中的读者和作者。 I took the alter database
suggestion 和 built a gem for ISO8601 intervals, ar_interval
.
它 provides a simple ActiveRecord::Type
为您处理 ISO8601 字符串的序列化和转换!
tests include examples for how to use it。
如果有兴趣,the additional formats Sam Soffes demonstrates 可能是 included in the tests
答案 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回来:/