Preloading in Ecto


I’m having some issues with association preloading in Ecto.

I have two models, Post can have many Images.

If I create a post and an image, Repo.insert(post_with_image) it returns a Post struct with %Post{images: [%Image{...}]}.

But if create a Repo.insert(post_without_image) it returnes %Post{images: #Ecto.AssociationNotLoaded....

In my PostView I do render_many(post.images, ImageView "image.json") which fails when I create a Post without an image.

It is optional for a user of my API to include an image.

How do you typically avoid this?

  • Check the value of post.images before rendering the images (if it’s an Ecto.AssociationNotLoaded, replace it with a [])? Feels a bit weird, because I use the same view to render the show action for instance, and then if I forget to preload the images I won’t notice that (I would in my tests probably but it would just render an empty list and not give me a good error message).
  • Preload after insert (seems dumb since it won’t have any images because we just created the post).
  • Something else?

Probably the easiest solution would be to use |> put_assoc(:images, []) in the “no images” changeset. This is explicit in that it automatically reveals that you indeed don’t want to insert images, and at the same time should solve the issue with the field after insertion.

Hmm, yeah, that’ll work for now. I actually don’t have two separate changesets for it since I just pass the params to the changeset and let it handle required/optional params. But I’ll squeeze it in there somewhere :slight_smile:


Post.changeset(%Post{}, param_changes)
|> put_assoc(:images, [])
|> Repo.insert()
|> etc...

You can add it in lots of places, does not need to be within the model. :slight_smile: