Ecto est capable de bien gérer des contraintes d’une base de données PostgreSQL : unique_violation
, foreign_key_violation
, exclusion_violation
et check_violation
.
Par contre, il ne gère pas la violation de contraintes personnalisées qu’il est possible de déclarer dans des fonctions appellées en tant que trigger. Par exemple, une fonction pourrait lever cette exception :
RAISE EXCEPTION 'Custom exception 👋' USING ERRCODE = 'integrity_constraint_violation';
Pour la convertir en erreur Ecto, par exemple dans une insertion, on doit capturer l’exception Postgrex.Error
levée par Ecto :
defmodule MyApp.Repo do
use Ecto.Repo,
adapter: Ecto.Adapters.Postgres,
otp_app: :my_app
defoverridable insert: 2
def insert(changeset, opts) do
super(changeset, opts)
rescue
exception in Postgrex.Error ->
handle_postgrex_exception(exception, __STACKTRACE__, changeset)
end
defp handle_postgrex_exception(%{postgres: %{code: :integrity_constraint_violation, message: message}}, _, changeset) do
changeset = Ecto.Changeset.add_error(changeset, :base, message)
{:error, %{changeset | valid?: false}}
end
defp handle_postgrex_exception(exception, stacktrace, _) do
reraise(exception, stacktrace)
end
end
Lorsqu’Ecto tente d’insérer un changeset dans la base de données et qu’une erreur Postgrex.Error
est levée, il tentera maintenant de la gérer lui-même, selon le code de violation lancée par notre fonction.
Si le code est géré, on peut ajouter une erreur personnalisée au changeset; sinon, l’erreur est relancée.
{:error, changeset} =
%MyApp.Record{}
|> Ecto.Changeset.change()
|> MyApp.Repo.insert()
changeset.errors # => [base: "Custom exception 👋"]