Is it possible to use an Ecto.Multi to do an insert and an insert_all where the insert_all needs the primary key resulting from the first insert? E.g. creating a parent record with numerous child records in one transaction.
The insert option can use a 3rd argument of changeset_or_struct_or_fun
, but the insert_all
function doesn’t allow for a function… so there doesn’t appear to be a good way to capture the primary key that gets generated.
I can wrap the steps inside of a Repo.transaction callback function like this:
Repo.transaction fn ->
with {:ok, parent} <- ParentContext.create(params),
{:ok, _children} <- create_children(parent, params)
do
else
{:error, e} -> Repo.rollback(e)
end
end
but I was curious if the multi supported it. Thanks!
1 Like
Yeah you can using Multi.merge/2
(or Multi.run
). It looks something like this:
Multi.new()
|> Multi.insert(:user, User.changeset(%User{}, params))
|> Multi.merge(fn %{user: user} ->
# That is the inserted user from the first part of the multi
Multi.new()
|> Multi.insert_all(:posts, build_insert_all_posts_changeset(user))
end)
|> Repo.transaction()
12 Likes
It is a function that you would need to define that returns a list of post changesets (and is suitable for insert_all so it might have to set inserted_at
and updated_at
).
How can you use insert_all
with a list of changeset?
You can’t directly. Repo.*_all
are lower level APIs, which don’t support many schema or changeset features. You can map things from changeset to values for insert_all
manually if you don’t need any of the not really supported parts.
Thanks for the clarification.
So, is there any example on how to insert a bunch of “children” in a Multi with validation?
Will open a new thread though. Don’t want to necrobump.