Search code examples
elixirphoenix-frameworkecto

Use Ecto.Multi to update a table twice in the same transaction


Consider a scenario where you are trying to model the logging of a bank transaction. A transaction has an amount, a type (credit or debit,) and is associated with an account.

In the case of adding a new bank transaction, I can do this, and it works well:

Multi.new()
|> Multi.insert(:transaction, transaction_changeset)
|> Multi.update(:account, account_changeset)
|> Repo.transaction()

Updating an existing bank transaction is a little more involved. That's because a user can not only change the amount, but also the account. Updating an existing transaction could mean removing the amount from the previous account, in addition to adding the amount to the current account. I tried this, but I'm not sure how to express what I'm trying to do using Ecto.Multi.

Multi.new()
|> Multi.update(:transaction, transaction_changeset)
|> Multi.update(:account, previous_account_changeset)
|> Multi.update(:account, current_account_changeset)
|> Repo.transaction()

Runtime error :account is already a member of the Ecto.Multi:


Solution

  • The issue here is that you are using the :account key twice. From the documentation:

    Ecto.Multi makes it possible to pack operations that should be performed in a single database transaction and gives a way to introspect the queued operations without actually performing them. Each operation is given a name that is unique and will identify its result in case of success or failure.


    You can choose whatever name you like as long as it's unique. Simply change your transaction to something like:

    Multi.new
    |> Multi.update(:transaction, transaction_changeset)
    |> Multi.update(:previous_account, previous_account_changeset)
    |> Multi.update(:current_account, current_account_changeset)
    |> Repo.transaction