In elixir, how put_assoc is supposed to be used?

For a project, I need to create a little appilcation in Elxir, Phoenix with Ecto as ORM.

I'm a complete beginner on this technologies and I'm stuck with a saving problem on a many-to-many association.

This is about a teams <-> users relationship where users can have multiple teams and teams can contains multiple users. For the moment, the db and objects seems good.

Here is my schemas :

Teams schema

 schema "teams" do
    field :description, :string
    field :title, :string
    field :date_creation, :naive_datetime
    belongs_to :manager, BackEndService.Users.User
    many_to_many :users, BackEndService.Users.User, join_through: "users_teams"

    timestamps(type: :utc_datetime)

Users schema

  schema "users" do
    field :username, :string
    field :email, :string
    field :hash_password, :string
    field :grade, :integer
    has_many :clocks, BackEndService.Clocks.Clock
    many_to_many :teams, BackEndService.Teams.Team, join_through: "users_teams"

    timestamps(type: :utc_datetime)

The real problem is when I try to add users to a team like below :

  1. First I get the users from a list :

    def get_users(ids) do
      users_from_list_query =
      from u in User,
      where: in ^ids
      |> Repo.preload([:teams])
  2. Then I try to insert them in a team :

    users_to_inject = Users.get_users(attrs.users_ids)
    team = get_team!(attrs.team_id)
    |> Repo.preload([:users, :manager])
    |> Ecto.Changeset.change
    |> Ecto.Changeset.put_assoc(:users, [users_to_inject])
    |> Repo.update!
  3. This leads to the following error :

** (ArgumentError) invalid changes being applied to changeset. Expected a keyword list or a map, got: [%BackEndService.Users.User{__meta__: #Ecto.Schema.Metadata<:loaded, "users">, id: 2, username: "Sec User", email: "[email protected]", hash_password: "$pbkdf2-sha512$160000$jUnAuyThExRqhgSfqCzT.Q$H4ReypEz5DDfqBcTqANbWe0t2Z0hOTM5.YXU2EJCvyV2PViVIPSmPEXPpaNRwhUxaRh59cQ1NwCP4SqBHSA9yQ", grade: 20, clocks: [], teams: [%BackEndService.Teams.Team{__meta__: #Ecto.Schema.Metadata<:loaded, "teams">, id: 1, description: "Rapide descriptiumlorem ipsum", title: "Équipe N1", date_creation: ~N[2023-10-25 15:05:34], manager_id: 1, manager: #Ecto.Association.NotLoaded<association :manager is not loaded>, users: #Ecto.Association.NotLoaded<association :users is not loaded>, inserted_at: ~U[2023-10-27 13:11:48Z], updated_at: ~U[2023-10-27 13:11:48Z]}, %BackEndService.Teams.Team{__meta__: #Ecto.Schema.Metadata<:loaded, "teams">, id: 2, description: "Rapide descriptiumlorem ipsum", title: "Équipe N2", date_creation: ~N[2023-10-25 15:05:34], manager_id: 1, manager: #Ecto.Association.NotLoaded<association :manager is not loaded>, users: #Ecto.Association.NotLoaded<association :users is not loaded>, inserted_at: ~U[2023-10-27 14:09:37Z], updated_at: ~U[2023-10-27 14:09:37Z]}], inserted_at: ~U[2023-10-26 14:38:32Z], updated_at: ~U[2023-10-26 14:38:32Z]}]

Is there something I missed ?

P.S : That's my first question on the plateform so don't hesitate to provides tips for better question redaction

[EDIT] : Even when users_to_inject contains only one entry the problem is still yhe same


  • I fixed multiple errors to resolve this situation:

    1. As @Yatender Singh mentionned in comments, I was wrapping something that doesn't needed it. In fact, the infos retreived from Repo.all() where already in the good format.
    2. My schema was missing a property on the users_teams relation. The :on_replace had to be set to my needs (here delete all previous data to replace by new ones). Which give the following line many_to_many :users, BackEndService.Users.User, join_through: "users_teams", on_replace: :delete
    3. Finally I was updating data with |> Repo.update! rather than |> Repo.update()