如何使用 Postgres 区间数据类型 Rails 6

How to use Postgres interval data type with Rails 6

根据这里的其他答案,我认为我可以这样做:

rails generate model Availability

class CreateAvailabilities < ActiveRecord::Migration[6.0]
  def change
    create_table :availabilities do |t|
      t.references :user,      null: false, foreign_key: true
      t.datetime   :starts_at, null: false
      t.interval   :duration,  null: false
      t.timestamps             null: false
    end
  end
end

Availability.rb

class Availability < ApplicationRecord
  attribute :duration, :duration 
  belongs_to :user
end

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)

rails console

Loading development environment (Rails 6.0.3.2)
> a = Availability.create(user: User.first, starts_at: Time.now, duration: 10.minutes)
  User Load (0.7ms)  SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT   [["LIMIT", 1]]
   (0.2ms)  BEGIN
  Availability Create (1.7ms)  INSERT INTO "availabilities" ("user_id", "starts_at", "duration", "created_at", "updated_at") VALUES (, , , , ) RETURNING "id"  [["user_id", 1], ["starts_at", "2020-08-09 18:50:37.732198"], ["duration", "PT10M"], ["created_at", "2020-08-09 18:50:37.747158"], ["updated_at", "2020-08-09 18:50:37.747158"]]
   (1.0ms)  COMMIT
> a.duration
=> 10 minutes

我以为我在家是自由的,但当我尝试这个时我得到了这个错误

> Availability.first.duration
  Availability Load (0.6ms)  SELECT "availabilities".* FROM "availabilities" ORDER BY "availabilities"."id" ASC LIMIT   [["LIMIT", 1]]
Traceback (most recent call last):
        3: from (irb):5
        2: from (irb):6:in `rescue in irb_binding'
        1: from config/initializers/duration_type.rb:5:in `cast'
ActiveSupport::Duration::ISO8601Parser::ParsingError (Invalid ISO 8601 duration: "00:10:00")

它似乎正确地创建了 Postgres 数据库条目,但我很困惑为什么它无法读回它

可能您需要更改持续时间在数据库中的保存方式。看起来像 00:10:00 这样存储的。但解析器期望字符串采用 ISO8601 格式 (PT10M)。

class ChangeIntervalStyleInDatabase < ActiveRecord::Migration[6.0]
  def self.up
    execute "ALTER DATABASE \"#{connection.current_database}\" SET intervalstyle = 'iso_8601'"
  end

  def self.down
    execute "ALTER DATABASE \"#{connection.current_database}\" SET intervalstyle = 'postgres'"
  end
end

Rails 6.1+

自从我最初的回答以来,Rails 6.1 已经发布,添加了对 PostgreSQL interval 数据类型的支持。这消除了对转换和序列化值的自定义类型的需要。