I recently updated a few :date
column types in one table to be :utc_datetime
, and have added a helper method to add hours + minutes to those fields in order to make them convertible to a :utc_datetime
if they come in with only MMDDYY set. It gets through the type casting / validations, but fails when it’s time to insert into the db (Repo.insert(changeset)
), with this error:
** (CaseClauseError) no case clause matching: {{{2010, 4, 17}, {0, 0, 0, 0}}}
Here are the relevant items from the model model:
schema "campaigns" do
field :start_date, :utc_datetime
field :end_date, :utc_datetime
field :due_date, :utc_datetime
field :original_copy, :string
...
timestamps(type: :utc_datetime)
end
@doc """
Builds a changeset based on the `struct` and `params`.
"""
def changeset(struct, params \\ %{}) do
struct
|> cast(utc_compatible_dates(params), [:start_date, :end_date, :due_date, :original_copy])
|> validate_required([:start_date, :end_date, :due_date, :original_copy])
end
defp utc_compatible_dates(params) do
midnight = %{hour: 0, minute: 0}
try do
Map.update!(params, :start_date, &Map.merge(midnight, &1))
|> Map.update!(:end_date, &Map.merge(midnight, &1))
|> Map.update!(:due_date, &Map.merge(midnight, &1))
rescue
# if one of these is missing, let the requirement validation catch it
KeyError -> params
end
end
here’s what happens, with full stacktrace:
attrs = %{ original_copy: "some content", end_date: %{day: 17, month: 4, year: 2010}, start_date: %{day: 17, month: 4, year: 2010}, due_date: %{day: 17, month: 4, year: 2010} }
changeset = Campaign.changeset(%Campaign{}, attrs)
#=>Ecto.Changeset<action: nil,
changes: %{due_date: #<DateTime(2010-04-17T00:00:00Z Etc/UTC)>,
end_date: #<DateTime(2010-04-17T00:00:00Z Etc/UTC)>,
original_copy: "some content", start_date: #<DateTime(2010-04-17T00:00:00Z Etc/UTC)>},
errors: [], data: #Jingle.Campaign<>, valid?: true>
iex(5)> Repo.insert(changeset)
** (CaseClauseError) no case clause matching: {{{2010, 4, 17}, {0, 0, 0, 0}}}
(ecto) lib/ecto/adapters/postgres/datetime.ex:40: Ecto.Adapters.Postgres.TypeModule.encode_params/3
(postgrex) lib/postgrex/query.ex:45: DBConnection.Query.Postgrex.Query.encode/3
(db_connection) lib/db_connection.ex:1071: DBConnection.describe_run/5
(db_connection) lib/db_connection.ex:1142: anonymous fn/4 in DBConnection.run_meter/5
(db_connection) lib/db_connection.ex:584: DBConnection.prepare_execute/4
(ecto) lib/ecto/adapters/postgres/connection.ex:93: Ecto.Adapters.Postgres.Connection.execute/4
(ecto) lib/ecto/adapters/sql.ex:243: Ecto.Adapters.SQL.sql_call/6
(ecto) lib/ecto/adapters/sql.ex:562: Ecto.Adapters.SQL.struct/7
(ecto) lib/ecto/repo/schema.ex:467: Ecto.Repo.Schema.apply/4
(ecto) lib/ecto/repo/schema.ex:205: anonymous fn/13 in Ecto.Repo.Schema.do_insert/4
(ecto) lib/ecto/repo/schema.ex:684: anonymous fn/3 in Ecto.Repo.Schema.wrap_in_transaction/6
(ecto) lib/ecto/adapters/sql.ex:620: anonymous fn/3 in Ecto.Adapters.SQL.do_transaction/3
(db_connection) lib/db_connection.ex:1275: DBConnection.transaction_run/4
(db_connection) lib/db_connection.ex:1199: DBConnection.run_begin/3
(db_connection) lib/db_connection.ex:790: DBConnection.transaction/3
Can anyone tell me where I’m going wrong? Happy to provide more info. I’m new to Phoenix / Elixir so I imagine I’m missing something obvious.