Search code examples
elixirecto

Cast association changeset for many to many


I have this many to many relation between users and facilities:

schema "facilities" do    
  field(:name, :string)
  field(:description, :string)    
  many_to_many(:users, User, join_through: "users_facilities")
end

schema "users" do
  field(:first_name, :string)
  field(:last_name, :string)
  field(:email_address, :string)
  field(:username, :string)
  many_to_many(:facilities, Facility, join_through: "users_facilities")
end

This is the changeset_params:

  %{
  name: john,
  users: [%{id: 1286, name: "john doe"}, %{id: 1287, name: "jane doe"}]
 }

It works fine for the has_many and give me a single changeset for the entire associations. but didn't work for many to many.

The records_struct have complete facilities association loaded with users This is the code:

    Facility.changeset(records_struct, changeset_params)
    |> Ecto.Changeset.cast_assoc(:users)

I get changeset returned from Facility.changeset but Ecto.Changeset.cast_assoc throws internal server error when I try to test it.


Solution

  • Your users and facilities are managed independently. In other words, you don't create a new facility within the user's changeset and vice-versa. You want just to bind/associate to already existing entities, right?

    For this case, you need Ecto.Changeset.put_assoc/4

    Remember that you will need the association to be preloaded before doing put_assoc. Also be sure to set on_replace option in your many_to_many associations, like:

    many_to_many(:users, User, join_through: "users_facilities", on_replace: :delete)
    

    For the many to many associations you most likely want a :delete. It means that when you pass associated records in the changeset it will remove associations with the records that were associated before but they are not in the current changeset. It will not delete associated records.