5

When loading date/time types from the database, Ecto will cast to a Ecto.DateTime type. How can the same type casting be applied when loading a model from a JSON string

defmodule Rocket.User do
  use Rocket.Model

  schema "users" do
    field :created_at, :datetime
    field :name, :string
    field :email, :string
    field :password, :string
    field :timezone, :string
  end
end

iex(40)> Poison.decode!(~s({"created_at":"2015-01-21T06:05:10.891Z"}), as: Rocket.User)  
%Rocket.User{created_at: "2015-01-21T06:05:10.891Z", email: nil, id: nil,
 name: nil, password: nil, timezone: nil}
Krut
  • 4,112
  • 3
  • 34
  • 42

1 Answers1

6

If you are using Ecto 0.6.0, the best way is to use changesets:

Ecto.Changeset.cast Poison.decode!(data), %Rocket.User{},
                    ~w(required_fields), ~w(optional_fields)

Using changeset is actually recommended if you are receiving this as external data since you need to cast, filter and validate this data before adding it to the model. You can find more information about them in the Ecto introduction and also in the Ecto.Changeset module documentation.

There is one issue left though: Ecto does not know how to cast the string to datetime. You can however teach it how to by using custom types. I have created a template below, you just need to implement the casting function:

https://gist.github.com/josevalim/1ed574b388c32f056da1

Then in your schema:

timestamps type: Rocket.DateTime

You can find more information in Ecto.Type docs. I am aware we need to improve this in Ecto, I think we should at least be able to parse datetimes in the format specified in JSON.

José Valim
  • 50,409
  • 12
  • 130
  • 115
  • thanks for the link to custom types, is this still the preferred method if the data is trusted. JSON in this case is being used to serialize/deserialize the models so programs in different languages can work with the data – Krut Jan 21 '15 at 15:14
  • This will work both ways even if the data is trusted. :) So you are fine. – José Valim Jan 23 '15 at 15:50
  • is this still valid, cast returns a changeset that I then need to apply to get the mode, seems expensive `Ecto.Changeset.cast(Poison.decode!(data), %Rocket.User{}, ~w(required_fields), ~w(optional_fields)) |> Ecto.Changeset.apply_changes` – Krut Oct 13 '15 at 20:45