Ok because of the multiple levels of returns I am getting a little lost.
I am very new to ecto so here goes.
I'm trying to wrap my account creation in a transaction because it creates many child records etc.
So I have this so far:
def create_account(company_name, ...) do
Repo.transaction(fn ->
case Account.create_account(%{
# ... attributes here
}) do
????
end
# insert other model records here using the same above case pattern matching
account
end) # transaction
end
The create_account on the ecto schema model looks like:
Account.ex
def create_account(attrs \\ %{}) do
%Account{}
|> Account.changeset(attrs)
|> Repo.insert()
end
So now there are 3 levels of return values which I am not sure how to handle all together:
the happy path of a transaction seems to return: {:ok, model}
if the account.create_account insert fails, how to pass that errors down to the final return value so I can display that in the UI?
how to correctly rollback in any of the steps?
You should use Repo.rollback on failures. The docs say The transaction will return the value given as {:error, value}
, so this can be done with pattern matching as you mention:
def create_account(company_name, ...) do
Repo.transaction(fn ->
account = case Account.create_account(%{ # ... attributes here }) do
{:ok, account} -> account
{:error, changeset} -> Repo.rollback(changeset)
end
# insert other model
{:ok, account}
end)
end
This way your function will return {:ok, account}
on success, and {:error, changeset}
on whatever failure it encounters. Because you're inserting multiple things you might want to differentiate them, maybe like so:
account = case Account.create_account(%{ # ... attributes here }) do
{:ok, account} -> account
{:error, changeset} -> Repo.rollback({:account, changeset})
end
case User.create_user(account, %{ # ... attributes here }) do
{:ok, user} -> :ok
{:error, changeset} -> Repo.rollback({:user, changeset})
end
In this case the function will return {:ok, account}
if everything goes right, {:error, {:account, account_changeset}}
, if account insertion fails, and {:error, {:user, user_changeset}}
if user insertion fails.