Put_assoc can update column in case of the same pk?

Ecto’s put_assoc as well as cast_assoc always work with a set of associations.

When you supply a list of associations to those functions it’ll

  • Create any new items (no match for their passed id or no id available)
  • Update any existing (preloaded) items (matched by their id)
  • Delete any existing (preloaded) items, which are no longer in the passed list
|> Ecto.Changeset.put_assoc(:contents, contents ++ post.contents)

This is therefore problematic, because you’re passing the existing contents + the ones you passed into, which might also share their primary key. That’s expected to break.

I’m wondering why you’re not using cast_assoc here, which would resolve you of the need to manually combine existing contents with the passed in contents.

def upsert_content(post, attrs) do
    post = Repo.preload(post, :contents)

    post
    |> Ecto.Changeset.cast(%{contents: attrs})
    |> Ecto.Changeset.cast_assoc(:contents)
    |> Repo.update()
  end