I know about Ecto.Multi but was just making sure I’m doing this correctly:
I have an organization table. When an organization is created, a calendar, forum, and wiki need to be created. There are calendar, forum, and wiki tables all with an organization_id column to relate it with the created organization. So the pseudo-code of essentially what I want would look like this:
def create_organization() do
org = Repo.insert!(%Organization{})
Repo.insert!(%Forum{organization_id: org.id})
Repo.insert!(%Calendar{organization_id: org.id})
Repo.insert!(%Wiki{organization_id: org.id})
# Need to return the organization, but have to make sure
# all of the other rows are created before returning
org
end
So my questions are:
Is this a sane approach?
Are there any better ways to do this or is this just how to create related rows in another table with Ecto?
The reason I’m asking this question is because all of the examples I’ve seen with put_assoc, cast_assoc, and Ecto.Multi only deal with creating a single related row based off of the result of the first insert (the organization in my case). I’m creating multiple rows and I haven’t seen this in my searches so hopefully this will show up for people that look for it
Ecto.Multi is certainly the way to go. It also doesn’t care what resources you create after each other. If you really want to be “non-specific” about the order of forum/calender/wiki within the ecto.multi you can look into Ecto.Multi.merge instead of Ecto.Multi.run.
To build associations you can also use Ecto.build_assoc(org, :forum) if the association is also part of your organisation schema.
Definitely agreed that Ecto.Multi is the way to go. It would look something like this:
def create_organization() do
alias Ecto.Multi
Multi.new
|> Multi.insert(:organization, %Organization{})
|> Multi.merge(fn %{organization: org} ->
organization_relation_multi(org.id)
end)
|> Repo.transaction()
end
def organization_relation_multi(org_id) do
alias Ecto.Multi
Multi.new
|> Multi.insert(:forum, %Forum{organization_id: org.id})
|> Multi.insert(:calendar, %Calendar{organization_id: org.id})
|> Multi.insert(:wiki, %Wiki{organization_id: org.id})
end
Then either all entries would fail to insert or all of them would be inserted together. I think you may know this but in your original example if the calendar failed to insert then you’d be left with an orphaned/invalid organization and forum.
@axelson is there any reason you are aliasing Ecto.Multi in each function rather than once above both functions (e.g. runtime efficiency or readability) ?
Thanks for the example code! I found this helpful as well
I didn’t want to write out the example containing module
I didn’t want to write Ecto.Multi every time
But I also wanted it to be obvious that I was referring to Ecto.Multi so that people following along wouldn’t have compilation errors by just referring to Multi in their code