Receiving a list of IDs in params for a has_many association

Say user input params (from a form, GraphQL document, etc.) look like below (let’s say a User has many Articles):

%{
  "name" => "John",
  "articles" => [5, 9, 18]
} 

or I can change it to below if that’s better:

%{
  "name" => "John",
  "articles" => [%{id: 5}, %{id: 9}, %{id: 18}]
} 

The user has selected a list of articles through a multi-select component.

How should I insert/update the association articles for the User?

There are a few ways.

  1. Use Nested Changesets
  2. Insert each item individually in a transaction. (You would need to insert the user first so you have it’s id, then each article).
  3. Use an EctoMulti.

The mutli is the same idea as 2 just a different way to do it.

If the user exists already you can upsert the user instead.

For 1 you can either do something like this:

Ecto.Changeset.cast(%User{}, changes, [:name])
|> Ecto.Changeset.cast_assoc(:articles)

Read more about cast assoc here https://link.medium.com/ZVBaeIrqM8

And check out the docs here https://hexdocs.pm/ecto/Ecto.Changeset.html

1 Like

I want to use changeset; I don’t really see a reason to go for the 2. or 3. suggestion here.

Giving a list of IDs won’t work for cast_assoc (e.g. [5, 9, 18]). This is what is sent from Absinthe.

I’m not sure how to best solve this problem.

Each element needs to be the shape of an article, then it should just work. Are you getting an error message?

I guess the question then is how to get that shape:)

I receive a list of IDs from Absinthe. Those articles always exist, because the user selected those from a multi-select component. I can’t just pass that list of IDs to cast_assoc. The changeset will be invalid if I do so for “articles” field.

I need to replace all the articles IDs a user has by the list of articles IDs received from the Absinthe query.

Eventually I loaded all those entities by the ID list with a query (where id in ^ids).
Then added those entities in the changeset via put_assoc.