i've been recently thinking about a solution when we have seperate schema for the UI/form layer and the database layer. we could have different validations but i want to inform the users when something in the database didn' t work like a unique constraint. I've spun up a simple example to illustrate how we could use the existing functions to solve this. In this example we would have a single name field for the ui input but store the name as firstname and lastname in the DB.
data = %{}
types = %{firstname: :string, lastname: :string}
db_changeset = fn params ->
{data, types}
|> Ecto.Changeset.cast(params, Map.keys(types))
|> Ecto.Changeset.validate_required([:firstname, :lastname])
end
data = %{}
types = %{name: :string}
ui_changeset = fn params ->
{data, types}
|> Ecto.Changeset.cast(params, Map.keys(types))
|> Ecto.Changeset.validate_required([:name])
end
ui_changes = ui_changeset.(%{name: "asdf"})
data = ui_changes |> Ecto.Changeset.apply_action!(:insert)
[firstname | lastname] = String.split(
data.name, " ", parts: 2)
db_changes = db_changeset.(%{firstname: firstname, lastname: lastname})
new_ui_changeset = Ecto.Changeset.traverse_errors(db_changes, fn _changeset, field, {msg, opts} ->
case field do
f when f in [:firstname, :lastname] ->
{:name, msg, opts}
_ -> []
end
end)
|> Enum.reduce(ui_changes, fn {_field, errors}, changeset ->
Enum.reduce(errors, changeset, fn {field, msg, opts}, acc ->
Ecto.Changeset.add_error(acc, field, msg, opts)
end)
end)
My proposal would be to introduce a new helper function which just moves the error fields. Something like this: