Efficiently creating Ecto records from JSON

Hello. I’m working on a caching library that reads and writes Ecto records to Memcached and I’m running into issues with “loading” the record.

Here’s the code I’m using:

Ecto.Repo.Schema.load(Ecto.Adapters.Postgres, schema, attributes)

Where schema is something like User and attributes is a map.

The problem is that attributes is loaded from JSON, which means values from datetimes (like created_at) are strings like “2017-02-19T19:55:52.059727Z” and then the function fails.

I know I can make my own custom type and define a dump/load function, but that seems pretty invasive and a bit disingenuous (Ecto isn’t using the custom type, my caching library is).

Does anyone have any tips or tricks to solve this? Thank you.

1 Like

For posterity, here’s what I ended up doing…

  def instantiate(schema, attributes) do
    source = schema.__schema__(:source)
    prefix = schema.__schema__(:prefix)
    attributes = Poison.decode!(attributes)
    Ecto.Schema.__load__(schema, prefix, source, nil, attributes, &custom_loader(&1, &2))
  end

  defp custom_loader(_, nil), do: {:ok, nil}
  defp custom_loader(:utc_datetime, value) do
    case DateTime.from_iso8601(value) do
      {:ok, datetime, _offset} -> {:ok, datetime}
      {:error, _reason} -> :error
    end
  end
  defp custom_loader(:date, value) do
    case Date.from_iso8601(value) do
      {:ok, date} -> {:ok, date}
      {:error, _reason} -> :error
    end
  end
  defp custom_loader(type, value) do
    Ecto.Type.adapter_load(Ecto.Adapters.Postgres, type, value)
  end

Works like a charm.

1 Like