Hi
I am trying to get my head around this problem for a couple of days but I can’t find an elegant solution. I’d love to get your help on this.
Here is a simplification of my data model:
- A Recipe has many Versions
- A Version has many Ingredients
- A Version has many Media
I have a domain context called “Kitchen” that I use to retrieve/delete/create/update recipes.
I have implemented 2 functions on it: get_recipe
and get_recipe_history
. The first simply retrieves a recipe, the second preloads, for a given recipe, all versions and for each, all ingredients & media.
My problem is that the Media schema has some computed fields that can’t be inferred with the database data alone. I need to call a method on my Media secondary context to populate them. (Those fields represents a public URL of the media.upload
stored in database, it uses some mix config values to generate them).
My code is currently:
def get_recipe_history(recipe_or_recipes) do
Repo.preload(recipe_or_recipes,
recipe_versions: [:media, :ingredients]
)
end
but this doesn’t populate what I need on Media structs.
I have therefore added this function, that any callers need to invoke before accessing the public URL of the media:
defp maybe_populate_upload_urls(media) when is_list(media) do
Enum.map(media, &maybe_populate_upload_urls/1)
end
defp maybe_populate_upload_urls(%Media{} = media) do
...
end
It works, but I find it quite inconvenient. I’d like to automatically provide to any callers the public URL of a Media
.
On the other hand, I have a Media secondary context with this code:
def all(%RecipeVersion{} = version, preload \\ []) do
Repo.all(assoc(version, :media))
|> maybe_populate_upload_urls()
|> Repo.preload(preload)
end
which works perfectly but I can’t see how to take advantage of that in my Kitchen context.
I hope all of this makes sense. I found it really difficult to find an elegant pattern to do data preloading with ecto. Your help would be really appreciated.
Thanks in advance.