Not so much a question as a discovery. This is true as of Ecto 2.2. If you need to perform an insert_all
and the target schema has embeds_many
to an embedded_schema
…here’s how to make it work.
defmodule ImageTest do
defmodule Image do
use Ecto.Schema
import Ecto
embedded_schema do
field :url, :string
field :purpose, :string
field :color, :string
end
def changeset(image, attrs), do: cast(image, attrs, [:url, :color, :purpose])
end
use Ecto.Schema
import Ecto
defmodule Migration do
use Ecto.Migration
def change do
create table(:image_test, primary_key: false) do
add :id, :binary_id, primary_key: true
add :images, :jsonb, null: false, default: fragment("'{}'::jsonb")
end
end
end
schema "image_test" do
embeds_many :images, Image
timestamps()
end
@required_fields ~w(images)a
def changeset(struct, params), do: cast(struct, params, @required_fields)
end
# Assumes a `Repo`.
images1 = [%{url: "giphy.com"}, %{color: "blue"}]
images2 = [%{url: "imgur.com"}, %{color: "red"}]
# This won’t work
# Repo.insert_all(ImageTest, %{images: images1, images2})
images1 = Enum.map(images1, &struct(ImageTest.Image, &1))
images2 = Enum.map(images2, &struct(ImageTest.Image, &1))
# This works.
Repo.insert_all(ImageTest, %{images: images1, images2})
It feels weird that this is what we have to do for this to work, but I’m happy to have discovered it.