I am trying to write a function that creates a transaction to insert a parent struct Parent
with a bunch of childrens Children
.
The input parameters are coming from a form, so everything’s a string, example:
%{
"parent_field" => "trytry",
"children" => %{
"0" => %{
"children_field" => "baebae"
},
"1" => %{
"children_field" => "ciacia"
}
},
}
What I did so far is something like this (NOT WORKING):
def insert_parent_and_children(params) do
parent_params = Map.delete(params, "children")
parent_changeset = Parent.changeset(%Parent{}, parent_params)
children_params = Map.get(params, "children") |> Map.values()
Multi.new()
|> Multi.insert(:parent, parent_changeset)
|> Multi.merge(fn %{parent: parent} ->
# Here I got the parent id, but how can I insert all the children with validation?
end)
end
I can’t use Multi.insert_all
because I would like to use the changests (I have to show errors on the UI form).
Thank you for any help
Why use Multi
instead of Ecto.Changeset.cast_assoc
on the parent changeset?
How can I put the parent id in the childrens with cast_assoc
though?
I thought about Multi just to have that, I may be wrong.
You don’t need to. If you use associations that will be handled for you automatically.
Ok I may be doing something wrong then, because if I try:
p = %{... the params ...}
Parent.changeset(%Parent{}, p) |> Ecto.Changeset.cast_assoc(:children)
The changeset is invalid with:
#Ecto.Changeset<
action: nil,
changes: %{
parent_field: "a field",
children: [
#Ecto.Changeset<
action: :insert,
changes: %{
children_field: "a children field",
},
errors: [parent_id: {"can't be blank", [validation: :required]}],
data: ....
valid?: false
>,
#Ecto.Changeset<
action: :insert,
changes: %{
children_field: "a children field",
},
errors: [parent_id: {"can't be blank", [validation: :required]}],
data: ....
valid?: false
>,
],
},
errors: [],
data: ...,
valid?: false
>
Drop the validate_required
for :parent_id
. The id is not known at the time of validation, but will be set later.
I usually rely solely on null: false
on the db column and assoc_constraint(changeset, :parent)
to catch issues of incorrect foreign keys if they happen to be manually supplied.
4 Likes
Thank you, now I am really embarassed
Working like a charm.