I have question on the limitations of embeds_many
. I want non-homogenous embeds many.
Despite being able to define how to handle casting from the with
option in embeds_many
, casting fails when the type for the field is :map
.
defmodule Container do
use Ecto.Schema
embedded_schema do
embeds_many :foos_and_bars, :map
end
end
defmodule Foo do
use Ecto.Schema
embedded_schema do
field :position, :integer
field :unique_data, :list
end
end
defmodule Bar do
use Ecto.Schema
embedded_schema do
field :position, :integer
field :more_unique_data, :bool
end
The issue arises when I need to call cast_embed
on the Container to validate foos_and_bars
container = %Container{foos_and_bars: [%Bar{position: 1, more_unique_data: true}]}
params = %{
foos_and_bars: %{
"1" => %{more_unique_data: false}
}}
Ecto.Changeset.cast(container, params, [])
|> Ecto.Changeset.cast_embed(:foos_and_bars, with: fn foo_or_bar, params ->
case foo_or_bar do
foo = %Foo{} -> Foo.changeset(foo, params)
bar = %Bar{} -> Foo.changeset(bar, params)
end
end)
The cast_embed
throws (UndefinedFunctionError) function :map.__schema__/1 is undefined (module :map is not available) :map.__schema__(:primary_key)
as it expects the field to be a schema not just a plain :map
.
My question is why is this the case? I understand that there may not be an obvious use for field backed by a database to need this type flexiblity but when changesets
are the first class supported method of input validation in Phoenix these issues arrive. The wider context is for my application we have an ordered list of inputs. Some those “inputs” are nested input lists. To maintain the order correctly inside of the inputs_for
we need to store an Enumerable with two types (a plain input or nested group of inputs). Problems arrise when casting the changeset as described above.
I can further understand why this error could get thrown if Ecto.Changeset.cast_embed
was not called alongside the with
option. The with
allows you to define how to handle casting for each item in the embeds. which in my opinion should allow me to have case that matches on type and delegates to those modules changeset functions.
embeds_many :foos_and_bars, :map
should either raise an exception or the case should be handled as described above.