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?

1 Like

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.

1 Like

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.