I have a form which collects data and that data is inserted into multiple db tables. If one of those operations fails, whole transaction should fail and none of them should be inserted into a db so I’m using Ecto.Multi.
Now, if one of the operations fails, Repo.transaction() sends an error and that error is shown to the user. That all works fine but how can I get errors if more then one operation failed?
Here’s a simplified code, without associations to make it clearer.
|> Ecto.Multi.insert(:album, album_changeset)
|> Ecto.Multi.insert(:track, track_changeset)
|> Ecto.Multi.insert(:artist, artist_changeset)
If there is something wrong when inserting album, transaction would fail, user would be returned to the form and there would be an error in the album field inside the form but if there was also an error when inserting track or album, those errors would not get shown at all, only the error from the first failed transaction gets shown so user could think everything is fine with those fields when in fact it’s not.
So, how can I collect and show errors from all failed transactions to the user?
I believe the transaction stops at the first error (as it cannot continue anyway) so what you ask for is not possible in a transaction.
I don’t know Ecto very well but I guess you could at least validate the 3 changesets beforehand to find most errors. That would not cover all possible errors but for a form I guess it’s good enough.
This is correct.
In general, if something fails during a database transaction (be it inside an
Ecto.Multi or by manually performing multiple SQL commands), the transaction as a whole is aborted.
Some databases allow you to automatically retry failed transactions, or to nest transactions (where innermore transactions might be retried a couple of times rather than the whole outermost transaction failing immediately).
However, not all databases that Ecto wants to support do, which is why Ecto does not expose this to the user.
I use JS to validate form fields on the front-end but I also wanted to have nice server-rendered errors as a backup but if not possible, it’s not a big deal.
Thanks for clearing it up for me.
This is what one would do with plain changeset validation. c.f. the Ecto.Changeset module.
So, I would have to validate changesets before Ecto.Multi as lud suggested, right? I like this idea, thanks!
Yes! Or possibly inside an
Ecto.Multi.run function at the start of the
Ecto.Multi pipeline, of course.