[Proposal] Remap changeset error helper

5 views
Skip to first unread message

Kevin Schweikert

unread,
Nov 14, 2025, 10:37:48 AMNov 14
to elixir-ecto
Hey,

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:

ui_changes
|> Ecto.Changeset.map_error(db_changes, :firstname, :name)
|> Ecto.Changeset.map_error(db_changes, :lastname, :name)

Thanks for reading!
Reply all
Reply to author
Forward
0 new messages