How to traverse and translate nested errors

1,103 views
Skip to first unread message

Vladislav Shcherbin

unread,
Feb 22, 2016, 10:30:23 PM2/22/16
to elixir-ecto
For example, we have Faculties and Specialities. Each Faculty has many Specialities.

If we insert Faculties and Specialities using Ecto 2.0 and have nested errors (in Specialities), how can we traverse and return translated errors?

My current code was:

changeset = Faculty.changeset(%Faculty{}, attrs["data"], :specialities, [])

case Repo.insert(changeset) do
{:ok, _model} -> {:reply, :ok, socket}
{:error, changeset} ->
{:reply, {:error, %{errors: translate_errors(changeset)}}, socket}
end
defp translate_errors(changeset) do
Ecto.Changeset.traverse_errors(changeset, &translate_error/1)
end

José Valim

unread,
Feb 23, 2016, 2:45:55 AM2/23/16
to elixi...@googlegroups.com
That's it. You need to make sure to import YourApp.ErrorHelpers or reference the translate_error function in it.



José Valim
Skype: jv.ptec
Founder and Director of R&D

--
You received this message because you are subscribed to the Google Groups "elixir-ecto" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-ecto...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-ecto/05e35881-5da6-4e6f-9e19-a979605e9dc5%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Vladislav Shcherbin

unread,
Feb 23, 2016, 3:29:57 AM2/23/16
to elixir-ecto, jose....@plataformatec.com.br
It works, but the problem is - how to get nested errors. Let me explain:

Imagine, we have two models: Faculty, Speciality

Faculty
simple model with one required field(title), length >= 6

defmodule Universities.Faculty do
use Universities.Web, :model

schema "faculties" do
field :title, :string

timestamps

has_many :specialities, Universities.Speciality
end

def changeset(model, params, assoc, opts \\ []) do
model
|> cast(params, [:title])
|> cast_assoc(assoc, opts)
|> validate_required([:title])
|> validate_length(:title, min: 6)
end
end

Speciality
simple model with one required field(title), length >= 10

defmodule Universities.Speciality do
use Universities.Web, :model

schema "specialities" do
field :title, :string

timestamps

belongs_to :faculty, Universities.Faculty
end

def changeset(model, params \\ %{}) do
model
|> cast(params, [:faculty_id, :title])
|> validate_required([:title])
|> validate_length(:title, min:
10)
end
end

Faculty can have many Specialities.

I need to validate and send response with errors if invalid.

Cases
1.
Insert single Faculty and no Specialities, valid one
changeset = Faculty.changeset(%Faculty{}, %{title: "Faculty title"}, :specialities, [])

Valid, no errors.

2. Insert single Faculty and no Specialities, invalid one
changeset = Faculty.changeset(%Faculty{}, %{title: "Fac"}, :specialities, [])

Invalid, length error. So, I traverse errors and send response:

%{errors: Ecto.Changeset.traverse_errors(changeset, &translate_error/1)}

Client receives:
errors = {
title: "should be at least 6 character(s)"
}

Works great.

3. Insert single Faculty and some Specialities, valid one's
changeset = Faculty.changeset(%Faculty{}, %{title: "Faculty", specialities: [
%{title: "Speciality one"},
%{title: "Speciality two"}
]}, :specialities, [])

Valid, no errors

4. [The problem one] Insert single Faculty and some Specialities, invalid one's
changeset = Faculty.changeset(%Faculty{}, %{title: "Faculty", specialities: [
%{title: "Short one"},
%{title: "Speciality two"}
]}, :specialities, [])

Invalid, first Speciality length error. If I use the same traverse function:
%{errors: Ecto.Changeset.traverse_errors(changeset, &translate_error/1)}

I will get:
%{errors: %{specialities: [%{}, %{}]}}

So, nested errors are not shown.

The expected output is:

errors = {
specialities: [
{
title: "should be at least 10 character(s)"
}
]
}

That's why the question:

How to traverse and send the output with translated errors in it?



José Valim

unread,
Feb 23, 2016, 3:34:13 AM2/23/16
to elixi...@googlegroups.com
Can you provide a sample app that reproduces the error? Or it could even be a failing test case to Ecto's suite. We have tests in Ecto that ensures your exact use case works as expected, so something else is likely happening.



José Valim
Skype: jv.ptec
Founder and Director of R&D

José Valim

unread,
Feb 23, 2016, 3:36:25 AM2/23/16
to elixi...@googlegroups.com

Vladislav Shcherbin

unread,
Feb 23, 2016, 4:36:54 AM2/23/16
to elixir-ecto, jose....@plataformatec.com.br
Yes, I recreated a new app and the same code worked.

So, I used deps.clean and deps.get and it worked in my app.

So strange, it didn't work the whole day, only reinstalling deps helped.

Huge thank you!
Reply all
Reply to author
Forward
0 new messages