Ecto 自定义类型:如何将模型中的字段表示为特定类型?
Ecto custom types: How do I represent a field in a model as a specific type?
我有一个架构,我希望其中一个字段以 %Cm{value: 1.0}
(厘米单位)的形式表示。
我定义了这个客户类型:
defmodule Db.Types.Cm do
alias Units.Cm
@behavior Ecto.Type
def type, do: :float
def cast(%Cm{value: integer}) when is_integer(integer) do
Cm.new(integer / 1.0)
end
def cast(val = %Cm{value: float}) when is_float(float) do
val
end
def cast(number) when is_float(number), do: Cm.new(number)
def cast(number) when is_integer(number), do: Cm.new(number / 1.0)
def cast(_), do: :error
def load(float) when is_float(float), do: Cm.new(float)
def dump(%Cm{value: float}) when is_float(float), do: float
def dump(%Cm{value: integer}) when is_integer(integer), do: (integer / 1.0)
def dump(_), do: :error
end
遵循文档中的这些指南 (https://hexdocs.pm/ecto/Ecto.Type.html):
type
should output the name of the DB type
cast
should receive any type and output your custom Ecto type
load
should receive the DB type and output your custom Ecto type
dump
should receive your custom Ecto type and output the DB type
以及以下架构:
defmodule Db.Block do
schema "blocks" do
field :max_depth, Types.Cm
timestamps()
end
@fields ~w(max_depth)a
def changeset(struct, params \ %{}) do
struct
|> cast(params, @fields)
end
end
现在我尝试将块保存到数据库:
defmodule Db.BlockHelpers do
def new_block(attributes \ %{}) do
block = Dict.merge(%{
max_depth: 2
}, attributes)
%Block{}
|> Block.changeset(block)
|> Repo.insert!
end
end
iex> new_block()
...> new_block(%{max_depth: Units.Cm.new(5.0)})
但是我不断收到错误消息:
** (CaseClauseError) no case clause matching: %Units.Cm{value: 2.0}
我已经尝试了各种方法的组合,但似乎无法做到正确。所以我不是 100% 确定我理解文档。
归根结底,我希望能够传递 %Block{max_depth: %Units.Cm{value: 1.0}}
形式的模型结构,其中 cm 值作为浮点数存储在数据库 (postgres) 中。
Dogbert 提供的正确答案:返回 value
而不是 {:ok, value}
。
我有一个架构,我希望其中一个字段以 %Cm{value: 1.0}
(厘米单位)的形式表示。
我定义了这个客户类型:
defmodule Db.Types.Cm do
alias Units.Cm
@behavior Ecto.Type
def type, do: :float
def cast(%Cm{value: integer}) when is_integer(integer) do
Cm.new(integer / 1.0)
end
def cast(val = %Cm{value: float}) when is_float(float) do
val
end
def cast(number) when is_float(number), do: Cm.new(number)
def cast(number) when is_integer(number), do: Cm.new(number / 1.0)
def cast(_), do: :error
def load(float) when is_float(float), do: Cm.new(float)
def dump(%Cm{value: float}) when is_float(float), do: float
def dump(%Cm{value: integer}) when is_integer(integer), do: (integer / 1.0)
def dump(_), do: :error
end
遵循文档中的这些指南 (https://hexdocs.pm/ecto/Ecto.Type.html):
type
should output the name of the DB typecast
should receive any type and output your custom Ecto typeload
should receive the DB type and output your custom Ecto typedump
should receive your custom Ecto type and output the DB type
以及以下架构:
defmodule Db.Block do
schema "blocks" do
field :max_depth, Types.Cm
timestamps()
end
@fields ~w(max_depth)a
def changeset(struct, params \ %{}) do
struct
|> cast(params, @fields)
end
end
现在我尝试将块保存到数据库:
defmodule Db.BlockHelpers do
def new_block(attributes \ %{}) do
block = Dict.merge(%{
max_depth: 2
}, attributes)
%Block{}
|> Block.changeset(block)
|> Repo.insert!
end
end
iex> new_block()
...> new_block(%{max_depth: Units.Cm.new(5.0)})
但是我不断收到错误消息:
** (CaseClauseError) no case clause matching: %Units.Cm{value: 2.0}
我已经尝试了各种方法的组合,但似乎无法做到正确。所以我不是 100% 确定我理解文档。
归根结底,我希望能够传递 %Block{max_depth: %Units.Cm{value: 1.0}}
形式的模型结构,其中 cm 值作为浮点数存储在数据库 (postgres) 中。
Dogbert 提供的正确答案:返回 value
而不是 {:ok, value}
。