bvobart
Ecto.Changeset - How do I make validation errors in embedded schemas show up on the parent?
Hi, I’m using Ecto embedded schemas for input validation of a JSON API. Basically, I have a Plug.Router implementation that accepts JSON and casts the input data into an Elixir struct with Ecto:
...
post "/validate" do
payload = conn.body_params
changeset = MyApp.Document.changeset(%MyApp.Document{}, payload)
if changeset.valid? do
document = Ecto.Changeset.apply_changes(changeset)
IO.inspect(document)
send_resp(conn, 200, "Payload: #{inspect(document)}\n")
else
IO.inspect(changeset.errors)
send_resp(conn, 400, "Error 400: Bad Request: #{inspect(changeset.errors)}\n")
end
end
...
My current implementation of the Ecto schema for MyApp.Document is shown below.
As you can see, a Document embeds a bunch of children, which for simplicity’s sake is now just Text elements.
defmodule MyApp.Document do
use Ecto.Schema
import Ecto.Changeset
@primary_key false
embedded_schema do
field :id, Ecto.UUID
field :type, :string
embeds_many :children, MyApp.Text
end
def changeset(document, attrs) do
document
|> cast(attrs, [:id, :type])
|> cast_embed(:children)
|> validate_required([:id, :type])
|> validate_inclusion(:type, ["https://spec.nldoc.nl/Resource/Document"])
end
end
defmodule MyApp.Text do
use Ecto.Schema
import Ecto.Changeset
@primary_key false
embedded_schema do
field :id, Ecto.UUID
field :type, :string
field :text, :string
end
def changeset(text, attrs) do
text
|> cast(attrs, [:id, :type, :text])
|> validate_required([:id, :type, :text])
|> validate_inclusion(:type, ["https://spec.nldoc.nl/Resource/Text"])
end
end
The casting in my router works in the sense that if I supply a document with an invalid id or where the type is incorrect, then changeset.errors in my router contains an error message and validation fails as expected (so 400 is returned).
However, if I have a similar problem in the any of the embedded objects, then the validation does indeed fail as expected, but changeset.errors in my router is empty.
How do I ensure that any errors originating from casting / validating embedded schemas (MyApp.Text), end up on the changeset returned by the parent (MyApp.Document)? Or how else do I ensure that changeset.errors in my router contains all validation errors, even from nested embeds?
Marked As Solved
LostKobrakai
You generally don’t do that. The errors of children are not meant to go on the key of the parent changeset.
They’re in parent_changeset.changes.children[*].errors. The children under changes are also changeset structs, where each has their own errors.
It’s not just that. It also traverses all the nested changesets within the parent and allows you to build a nested structure of all their errors. The result is likely what you want to return instead of changeset.errors.
Also Liked
mathieuprog
Did you look into this?
https://github.com/mathieuprog/polymorphic_embed/blob/v5.0.0/README.md#traverse_errors2
bvobart
Hi, thanks for your quick reply! Yes, I did find PolymorphicEmbed.traverse_errors/2 and I tried it (I even examined its implementation), but it can’t iterate over errors in a child changeset that doesn’t exist ![]()
I’ve created a minimal working example in a GitHub repo here just to illustrate the point a bit better:
https://github.com/bvobart/polymorphic-embed-errors-mwe
See lib/schema.ex and test/schema_test.exs to see what I mean. I’ve added some tests that show the current behaviour and the behaviour that would be desirable (and that would be more akin to what Ecto does for embeds_many).
Do you want to continue this conversation here, or should I make an issue on the PolymorphicEmbed repo and continue there?
Last Post!
bvobart
Hi, thanks for your quick reply! Yes, I did find PolymorphicEmbed.traverse_errors/2 and I tried it (I even examined its implementation), but it can’t iterate over errors in a child changeset that doesn’t exist ![]()
I’ve created a minimal working example in a GitHub repo here just to illustrate the point a bit better:
https://github.com/bvobart/polymorphic-embed-errors-mwe
See lib/schema.ex and test/schema_test.exs to see what I mean. I’ve added some tests that show the current behaviour and the behaviour that would be desirable (and that would be more akin to what Ecto does for embeds_many).
Do you want to continue this conversation here, or should I make an issue on the PolymorphicEmbed repo and continue there?
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #elixirconf-us
- #advent-of-code
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #hex









