Ecto: insert item with assoc (details in body)

This seems like an easy query (and it is) but my Google fu is completely failing me for some reason.

I have a model tag and a model post and I need to create a post, with a tag. Tag <-> Post is a many_to_many assoc, so I cannot just set tag_id to post for example

  student = %Student{}
  |> Student.changeset(attrs)
  |> Ecto.Changeset.put_assoc(:class_lists, [class_list])
  |> Repo.insert()

This does not work, because of a class_list_pkey error - assumedly since Repo.insert tries to insert the class_list as well. However, I cannot just use Repo.update, because I do need to insert the student. What’s the idiomatic way to do this in Ecto?

Right now I’m just running a transaction. Is this really the intended way? I imagine I’m just missing something.

Please note:

“This function should be used when working with the entire association at once (and not a single element of a many-style association) and using data internal to the application.”

https://hexdocs.pm/ecto/Ecto.Changeset.html#put_assoc/3

This is the ecto documentation example:

tags = Repo.all(from t in Tag, where: t.name in ^params["tags"])

user
|> Repo.preload(:tags)
|> Ecto.Changeset.cast(params) # No need to allow :tags as we put them directly
|> Ecto.Changeset.put_assoc(:tags, tags) # Explicitly set the tags

As you can see, the example works with the entire User association and preloads the tags assosiated to it. So you have a user with tags.

In the other hand you have a new tags list which are associated with to the user in the put_assoc function.

In your situation, you must first load the class_list similar to the example.

hope this helps.

Best regards

3 Likes