I have a classic scenario when I can’t delete a Client when they have Projects assigned to them. For the check I have a no_assoc_constraint(:projects) on them and it generates the correct error, but the question is:
What is idiomatic way of turning this changeset errors into a message for the alert?
The changeset.errors outputs:
[projects: {"are still associated with this entry", []}]
I think I want it something like “Projects are still associated with this entry”.
From what I’ve done you can either iterate on changeset.errors to build your formatted string or use the message option when setting the constraint: no_assoc_constraint(:projects, message: "Projects are still associated with this entry").
Yep, that’s one way of doing it. I was wondering if there’s anything in the same line with full_messages in Rails. Something that converts the names of fields and error messages into strings using Gettext maybe.
defmodule MyAppWeb.Errors do
# Returns the list of full-text messages concatenating the names of fields
# and error messages taken from the changeset errors.
#
# Field names are translated using Gettext's "schema" domain. The key is
# formed from the name of the data structure (MyApp.MyContext.MyModel,
# for example: MyApp.Accounts.User) and the name of the field the error
# is related too (for `email` it becomes `MyApp.Accounts.User.email`
def full_messages(%Ecto.Changeset{} = changeset) do
module_name =
changeset.data.__struct__
|> Module.split
|> Enum.join(".")
changeset.errors
|> Enum.map(fn {key, error} ->
key_path = "#{module_name}.#{key}"
key_name = case Gettext.dgettext(MyAppWeb.Gettext, "schema", key_path) do
^key_path -> key
n -> n
end
"#{key_name} #{MyAppWeb.ErrorHelpers.translate_error(error)}"
end)
end
end
Yes, traverse_errors/2 is actually recommended because it correctly handles association and embed errors (they’re stored in changeset.changes.some_assoc.errors, not in changeset.errors)
Using Phoenix.Naming to humanize when there isn’t a translation available is also good idea in case the symbol includes underscores. E.g, :verify_email => Verify email