【问题标题】:Polymorphic embedded JSON value types?多态嵌入式 JSON 值类型?
【发布时间】:2016-09-18 19:37:13
【问题描述】:

我有一个应用程序,用户可以在其中发送自定义数据,并将其存储在我的应用程序中。目前我做这样的事情:

defmodule MyApp.CustomField do
  use Ecto.Schema
  import Ecto.Changeset
  alias MyApp.{ Time }


  defmodule ValueTypes do
    def c_INTEGER, do: "integer"
    def c_BOOLEAN, do: "boolean"
    def c_STRING,  do: "string"


    def c_TRUE,    do: "true"
    def c_FALSE,   do: "false"
  end


  embedded_schema do
    field :field
    field :value
    field :type
  end


  @required_fields ~w( field value )
  @optional_fields ~w( type )


  def changeset(model, params \\ :empty) do
    {:ok, value, type} = cast(model.field, model.value)
    params = %{value: value, type: type}

    model
    |> cast(params, @required_fields, @optional_fields)
    |> validate_length(:field, min: 1, max: 20)
    |> validate_length(:value, min: 1, max: 255)
    |> put_change(:type, type)
  end

  # date in unix timestamp
  def cast(field, value) when is_integer(value) do
    value = Integer.to_string(value)
    case String.slice(field, -3, 3) do
      "_at" -> {:ok, value, ValueTypes.c_DATE}
      _     -> {:ok, value, ValueTypes.c_INTEGER}


    end
  end

  def cast(field, value) when is_boolean(value), do: {:ok, (if value, do: ValueTypes.c_TRUE, else: ValueTypes.c_FALSE), ValueTypes.c_BOOLEAN}
  def cast(field, value) when is_binary(value),  do: {:ok, value, ValueTypes.c_STRING}
  def cast(field, value), do: {:error, "Invalid field value"}


end

目前我将所有内容保存为字符串并保留一个类型字段以在我的应用程序和数据库之间转换数据类型。此外,我必须将所有内容都转换为字符串,因为 Ecto (AFAIK) 不支持在嵌入式 JSON 中包含多态类型。

这对于索引字段和值是否有问题?

我曾想过使用自定义 Ecto 类型,但这是不可能的,因为我在转换函数中依赖于两个值(转换日期时,我会查看字段名称末尾的“_at”)

有没有更好的方法来做到这一点?

【问题讨论】:

    标签: elixir phoenix-framework ecto


    【解决方案1】:

    如果您使用 Postgres 作为数据库,那么您想要的行为已经存在。您将需要在架构中使用 :map 数据类型。您可以执行以下操作:

    defmodule MyApp.CustomField do
      use Ecto.Schema
      import Ecto.Changeset
      alias MyApp.{ Time }
    
      embedded_schema do
        field :data, :map
      end
    end
    

    然后您可以使用%CustomField{data: %{"field" => "custom", "value" => 1}} |> Repo.insert!1 这里将作为整数存储在您的 data 列中的 JSON 中。当您从数据库中获取此记录时,Ecto 将使用像 Poison 这样的 JSON 序列化程序来解码 JSON 数据,并将 1 作为 Elixir 中的整数返回。

    【讨论】:

    • 但是这样就不可能进行任何验证,是吗?
    • 仍然可以使用它进行验证。你有什么样的验证?你他们没有工作吗?
    • 例如值的长度
    • 您可以使用hexdocs.pm/ecto/Ecto.Changeset.html#validate_change/3 为您的变更集编写自定义验证。您使用它而不是使用仅适用于字符串和列表的 validate_length/3。
    • 如果您认为我的回答可以解决您的问题,请接受它作为答案。 :)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-20
    • 2020-12-07
    • 1970-01-01
    • 2021-03-11
    相关资源
    最近更新 更多