Validate that an `embeds_many` field is present in the changeset

With these modules:

defmodule Foo do
  use Ecto.Schema
  import Ecto.Changeset

  schema "foo" do
    field(:name, :string, null: false)
    embeds_many(:bars, __MODULE__.Bar, on_replace: :delete)

  def changeset(model, params \\ %{}) do
    |> cast(params, [:name])
    |> cast_embed(:bars)
    |> validate_required([:name, :bars])

defmodule Foo.Bar do
  use Ecto.Schema

  embedded_schema do
    field(:bar, :string, null: false)

I expect the following line to return a “can’t be blank” error for the :bars field:

%Foo{} |> Foo.changeset(%{name: "foo"})

But instead, I got a valid changeset.

How can I make sure that :bars is present in the changeset?

I already tried cast_embed(:bars, required: true) but it doesn’t help.

In fact, :bars is defined:

> %Foo{}

  __meta__: #Ecto.Schema.Metadata<:built, "foo">,
  bars: [],
  id: nil,
  name: nil

So I don’t get why %Foo{} |> Shipfixcore.Repo.insert produces a:

** (Postgrex.Error) ERROR 23502 (not_null_violation) null value in column "bars" violates not-null constraint

    table: foo
    column: bars

But I guess it has nothing to do with embeds_many.

I opened an other thread to get help for the underlying issue: How to persist an empty list in a `jsonb` column?
Maybe this one should be closed?

How about adding it a default value of [] in the migration?

You can then also use Ecto.Changeset.validate_length if you don’t want to allow a non-empty list being inserted or updated, I suppose.

That’s what I ended up doing :+1:
I opened an issue on the Ecto repo to indicate this behaviour: