Creating two models then associating with validation error

Hi everyone

I have encountered a small issue with my Phoenix code. I have three models, Recipe, Ingredient, and RecipeIngredient. The relationship between them is many to many, and the Recipe and Ingredient are “joined” through RecipeIngredient.

When the validation goes fine for the recipe, everything works fine. However, when there is a validation error with the recipe, the ingredients are inserted, but obviously no association takes place (i.e. no recipe ingredients are created, nor the recipe). I would prefer it if in the event of a validation error no related models were inserted.

The logic for creating the recipe is:

def create_recipe(attrs \\ %{}) do
	change_recipe(%Recipe{}, attrs)
	|> Repo.insert()
end

And the logic for associating the two models is:

def change_recipe(%Recipe{} = recipe, attrs \\ %{}) do
    Recipe.changeset(recipe, attrs)
    |> Ecto.Changeset.put_assoc(:ingredients, process_ingredients(attrs))
end

def process_ingredients(params) do
	(params["ingredients"] || [])
	|> remove_empty_strings()
	|> Enum.map(&get_or_insert_ingredient/1)
end

defp get_or_insert_ingredient(ingredient) do
	Repo.insert!(
		%Ingredient{ingredient: ingredient},
		on_conflict: [set: [ingredient: ingredient]],
		conflict_target: :ingredient
	)
end

Ecto multi run may help you achieve something like this https://hexdocs.pm/ecto/Ecto.Multi.html

Description from the doc:

The function given to run must return {:ok, value} or {:error, value} as its result. Returning an error will abort any further operations and make the whole multi fail.

2 Likes

Thanks @wolfiton

1 Like

Glad i could help