How to update a many-to-many association with Ecto

I have a Phoenix app with two resources that have a many to many relationship. To simplify, let’s call them posts and tags. When I create a post from scratch, I can associate existing tags with it. I can also edit a post that has no tags to associate tags with it. However, whenever I want to edit which tags are associated with a post, I get this error:

you are attempting to change relation :tags of MyApp.Posts.Post but the `:on_replace` option of
this relation is set to `:raise`

Here is my changeset - I’m using put_assoc which has 4 arguments, but according to the docs, the opts one isn’t used so I can’t set the on_replace option:

def changeset(%__MODULE__{} = user, attrs) do
    tag_ids = if attrs["tags"], do: attrs["tags"], else: []
    tags = Tags.by_ids(tag_ids)

    user
    |> Repo.preload(:tags)
    |> cast(attrs, [:title, :body])
    |> put_assoc(:tags, tags)
    |> validate_required([:body])
  end

The idea is to only update the association between the two - I would never create or delete tags themselves from the posts form. Should I be updating them in a different way?

(cross-posted on stackoverflow)

1 Like

on_replace option is set when you define many_to_many relationship.
You can set it to :delete if that is the desired outcome - that will delete the previous tags that are not passed with current params and insert only the current ones.
You can check the behaviour here: https://hexdocs.pm/ecto/Ecto.Changeset.html#module-the-on_replace-option

1 Like

Thanks! I was thinking the error was stating to pass it as an option to put_assoc instead. I’ll try this. I didn’t make a schema for the join table itself so I imagine I put this on the posts table for the many-to-many line about tags?

Yes, exactly

1 Like