This is what I had to do.
blog = Repo.get!(Blog, 1) |> Repo.preload([:user_ids])
user_ids = blog.user_ids |> Map.filter(fn user_id -> user_id.id in [ 2, 3, 4 ] end)
Repo.get!(Post, 1)
|> Repo.preload(:user_ids)
|> put_assoc(:user_ids, user_ids)
|> Repo.update()
There’s a hack with put_assoc to update the join table from params without loading user_ids.
This may be the reason it’s not possible with cast_assoc.
Yes, because allowing that would be a security risk. For example, imagine you have manager table which has many projects. If I can assign any random ID, I could get a project that does not long to this org and assign it to my manager. Depending on how the query is structured, then I get visibility in projects from other orgs.
There could be ways to limit this but the best is to close the attack vector altogether and simply not allow this feature via cast_assoc. You can associate them in other, more explicit, ways.