Reputation: 24561
I have a Postgres table with a tsrange
column, and I'd like to include that in my Ecto module's schema
. I see that Postgrex.Range
exists. I've tried this:
schema "clients" do
field :valid_at, Postgrex.Range
...
end
But that gives me this error:
** (ArgumentError) invalid or unknown type Postgrex.Range for field :valid_at
lib/ecto/schema.ex:1785: Ecto.Schema.check_type!/3
lib/ecto/schema.ex:1473: Ecto.Schema.__field__/4
Any suggestions? I'm using Phoenix 1.3 and the Ecto master branch.
Upvotes: 1
Views: 464
Reputation: 24561
Looks like @TheAnh has the right approach, but here is what actually wound up working for me:
defmodule Myapp.TsRange do
@behaviour Ecto.Type
def type, do: :tsrange
def cast(nil), do: {:ok, nil}
def cast([lower, upper]), do: {:ok, [lower, upper]}
def cast(_), do: :error
def load(%Postgrex.Range{lower: lower, upper: upper}) do
lower = lower |> to_datetime
upper = upper |> to_datetime
case [lower, upper] do
[nil, nil] -> {:ok, [nil, nil]}
[{:ok, lower}, {:ok, upper}] -> {:ok, [lower, upper]}
_ -> :error
end
end
def load(_), do: :error
def dump([lower, upper]) do
{:ok, %Postgrex.Range{lower: lower |> from_datetime,
upper: upper |> from_datetime,
upper_inclusive: false}}
end
def dump(_), do: :error
defp to_datetime(nil), do: nil
defp to_datetime({{y, m, d}, {h, min, s, ms}}) do
NaiveDateTime.new(y, m, d, h, min, s, ms)
end
defp from_datetime(nil), do: nil
defp from_datetime(dt) do
{{dt.year, dt.month, dt.day}, {dt.hour, dt.minute, dt.second, elem(dt.microsecond, 0)}}
end
end
Upvotes: 1
Reputation: 2813
I think you should create a custom type for tsrange
to work with Ecto.
defmodule YourApp.TimestampRange do
@behaviour Ecto.Type
def type, do: :tsrange
def cast([lower, upper]) do
{:ok, [lower, upper]}
end
def cast(_), do: :error
def load(%Postgrex.Range{lower: lower, upper: upper}) do
{:ok, [lower, upper]}
end
def dump([lower, upper]) do
{:ok, %Postgrex.Range{lower: lower, upper: upper, upper_inclusive: false}}
end
def dump(_), do: :error
end
About inclusive boundaries checkout PostgreSQL documentation
and then in your app you can use:
schema "clients" do
field :valid_at, YourApp.TimestampRange
...
end
Upvotes: 3