Implicit cast_assoc insertion order causes unpredictable constraint error

With the following schema

defmodule Grid do
  use Ecto.Schema
  import Ecto.Changeset

  schema "grids" do
    has_many(:xs, Grid.X)
    has_many(:ys, Grid.Y)
    has_many(:points, Grid.Point)
  end

  def changeset(model, params) do
    model
    |> cast(params, [:uuid])
    |> cast_assoc(:points)
    |> cast_assoc(:xs)
    |> cast_assoc(:ys)
  end
end


defmodule Grid.X do
  use Ecto.Schema
  import Ecto.Changeset

  schema "grid_xs" do
    has_many(:points, Grid.Point)
    belongs_to(:grid, MeasureTable)
  end

  def changeset(model, params) do
    model
    |> cast(params, [:uuid, :grid_id, :value])
    |> validate_required([:grid_id])
    |> foreign_key_constraint(:grid_id)
  end
end


defmodule Grid.Y do
  use Ecto.Schema
  import Ecto.Changeset

  schema "grid_ys" do
    has_many(:points, Grid.Point)
    belongs_to(:grid, MeasureTable)
  end

  def changeset(model, params) do
    model
    |> cast(params, [:uuid, :grid_id, :value])
    |> validate_required([:grid_id])
    |> foreign_key_constraint(:grid_id)
  end
end


defmodule Grid.Point do
  use Ecto.Schema
  import Ecto.Changeset

  schema "grid_points" do
    field(:value, :float)

    belongs_to(:x, Grid.X)
    belongs_to(:y, Grid.Y)
    belongs_to(:grid, MeasureTable)
  end

  def changeset(model, params) do
    model
    |> cast(params, [:uuid, :x_id, :y_id, :grid_id, :value])
    |> validate_required([:x_id, :y_id, :grid_id])
    |> foreign_key_constraint(:x_id)
    |> foreign_key_constraint(:y_id)
    |> foreign_key_constraint(:grid_id)
  end
end

I’m able to create the whole grid structure using a single changeset, defined at Grid.changeset/2. Under the hood I can see Ecto doing the each insertion in sequence, starting at grids, then trying to insert points relation. At first this wouldn’t work, since the IDs are not yet defined. But I can generate it in code and populate the data structure with the IDs of each relation.

However since points has a foreign_key constraint with x and y, and Ecto starts the insertion with it, x's and y's are not yet defined, and the insertion fails.

But if I re-order the Grid schema definition, putting the points relation first, like this:

  schema "grids" do
    has_many(:points, Grid.Point)
    has_many(:xs, Grid.X)
    has_many(:ys, Grid.Y)
    # has_many(:points, Grid.Point)
  end

Since Ecto starts from bottom to top, the insertion succeeds since the entries are present in the database.

My proposal is to make this type of behavior explicitly controlled by the developer. It could be done by adding a depends field to cast_assoc, so a explicit order of insertion can be defined.