Cast_assoc creating nested changeset even when empty

I have a cast_assoc for a nested changeset that works as expected creating/updating the association. But if the parent fails to save (some other validation fails), the cast_assoc is creating an empty changeset for this association. This empty data prevents the data from being saved in the future, and it makes my form suddenly show lots of errors for something the user hasn’t interacted with.

Is there a way to make cast_assoc only create the nested changeset if there’s relevant data in the attrs? I could wrap the call to cast_assoc with a check to see if the key exists.

1 Like

The documentation provides the following example:


User
|> Repo.get!(id)
|> Repo.preload(:addresses) # Only required when updating data
|> Ecto.Changeset.cast(params, [])
|> Ecto.Changeset.cast_assoc(:addresses, with: **&MyApp.Address.changeset/2**)

So you can use a custom function to conditionally add the association depending on whether it’s empty or not.

Here’s the custom function they have:

def changeset(struct, params) do
  struct
  |> cast(params, [:title, :body])
  |> validate_required([:title, :body])
  |> case do
    %{valid?: false, changes: changes} = changeset when changes == %{} ->
      # If the changeset is invalid and has no changes, it is
      # because all required fields are missing, so we ignore it.
      %{changeset | action: :ignore}
    changeset ->
      changeset
  end
end
2 Likes

Ah great catch- I didn’t see that when I was scouring the docs. Thank you!