带有 tsrange 字段的 Ecto 模式
Ecto schema with a tsrange field
我有一个 Postgres table 和 tsrange
column, and I'd like to include that in my Ecto module's schema
. I see that Postgrex.Range
exists。我试过这个:
schema "clients" do
field :valid_at, Postgrex.Range
...
end
但这给了我这个错误:
** (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
有什么建议吗?我正在使用 Phoenix 1.3 和 Ecto master 分支。
我认为您应该为 tsrange
创建自定义类型以与 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
关于包含边界检查PostgreSQL documentation
然后在您的应用中您可以使用:
schema "clients" do
field :valid_at, YourApp.TimestampRange
...
end
看起来@TheAnh 的方法是正确的,但实际上这对我有用:
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
我有一个 Postgres table 和 tsrange
column, and I'd like to include that in my Ecto module's schema
. I see that Postgrex.Range
exists。我试过这个:
schema "clients" do
field :valid_at, Postgrex.Range
...
end
但这给了我这个错误:
** (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
有什么建议吗?我正在使用 Phoenix 1.3 和 Ecto master 分支。
我认为您应该为 tsrange
创建自定义类型以与 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
关于包含边界检查PostgreSQL documentation
然后在您的应用中您可以使用:
schema "clients" do
field :valid_at, YourApp.TimestampRange
...
end
看起来@TheAnh 的方法是正确的,但实际上这对我有用:
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