endymion

endymion

Why can't we have embeds_many :foo, :map where the embeds is a list of non-homogenous maps?

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.

Marked As Solved

al2o3cr

al2o3cr

This sounds like exactly what polymorphic_embed was designed to do:

Without the “type” field that that uses, it’s not possible to figure out which schema the results should be when retrieving a schema that says embeds_many

If you really care exclusively about writing these shapes, consider saying field :foos_and_bars, :map and then manually using Foo and Bar’s changesets to produce maps in cast_embed.

Last Post!

endymion

endymion

Nice library, interesting that Ecto doesn’t support this behavior itself though. The polymorphic embeds inputs_for is a nice feature to support as well to easily switch off type. That is my exact use case.

Where Next?

Popular in Questions Top

joaquinalcerro
Hi there, I am working with Ecto-Postgresql and I need to call all of the records from a specific table but the table has 40,000 records...
New
hariharasudhan94
lets say i have a sample like a = 20; b = 10; if (a > b) do {:ok, "a"} end if (a < b) do {:ok, b} end if (a == b) do {:ok, "equa...
New
skosch
To my knowledge, put_in, Map.update etc. all have the one limitation of not automatically creating intermediate keys when needed (for exa...
New
sergio_101
I am VERY much an elixir newbie. I have taken one elixir course and one phoenix course on Udemy. During that course, I saw the instructor...
New
shijith.k
I am trying to start a new phoenix project with elixir 1.9, but mix phx.new does not work. It says that ** (Mix) The task "phx.new" could...
New
stefanluptak
Hello everybody, usually, I use a 29" ultra-wide monitor for VSCode which can easily accomodate explorer (files panel) + file with code ...
New
jason.o
In the code below, if the create action is not set to accept “extra_key” as an input, it errors out with a message shown above. Is there ...
New

Other popular topics Top

electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
New
Darmani72
If I have a post route which an argument: post /my_post_route/:my_param1, MyController.my_post_handler How would get the post params ...
New
joeerl
Hello again - after a longish gap I’ve decided I really must dig into Elixir and see what’s been happening here - so I have a few questio...
New
gshaw
What is the idiomatic way of matching for not nil in Elixir? E.g., First way: defp halt_if_not_signed_in(conn, signed_in_account) when...
New
AstonJ
Posting this to see if we can make things easier for people to get into Neovim. If you use Neovim and have a favourite distro please let ...
New

We're in Beta

About us Mission Statement