Checking foreign key constraint when attempting to delete structure in has_many association

Hello,

I have a User structure that can have multiple accounts (has_many) and can be in multiple team through this accounts (many to many), as you can see below:

schema "user" do
    ...

    has_many :accounts, Account

    many_to_many(
      :teams,
      Team,
      join_through: Account
    )

    timestamps()
  end

And since I’m building basic CRUD operations for all entities of my application, I need to tell the user why he can’t delete his User (because there are still associated Accounts to it). But I can’t find a way to do this without try catching this way:

   try do
      Repo.delete(user)
    rescue
      Ecto.ConstraintError ->
        {:error, "In order to delete an user, you must first delete it's associated accounts"}
    end

But this is bad for 2 reasons, first is that it’s not standard that we use in our applications try catch (or erlang world), and second is that if I have more than one foreign constraint in any of my entities, I can’t tell which of them resulted in error.

I have tried all the possible ways found in documentation, for example:

on deletion:

Repo.get(User, user.id)
    |> Ecto.Changeset.change
    |> Ecto.Changeset.no_assoc_constraint(:accounts)
    |> Repo.delete()

on changeset validation:

user
|> Ecto.Changeset.no_assoc_constraint(:accounts)

user
|> foreign_key_constraint(:accounts, name: "johan_sports_team_users_user_id_9294a0f7_fk_auth_user_id")

I tried many variations of this, but nothing seems to help, in the end I always get:

** (Ecto.ConstraintError) constraint error when attempting to delete struct:

     * johan_sports_team_users_user_id_9294a0f7_fk_auth_user_id (foreign_key_constraint)

 If you would like to stop this constraint violation from raising an
 exception and instead add it as an error to your changeset, please
 call `foreign_key_constraint/3` on your changeset with the constraint
 `:name` as an option.

 The changeset has not defined any constraint.

Does anyone know a better solution for this?

Honestly I am not an Ecto expert, but it seems to me that what you do should work:

user
|> Ecto.Changeset.change
|> Ecto.Changeset.no_assoc_constraint(:accounts)
|> Repo.delete

My only doubt is that you might have to specify the name of the foreign key, like Ecto.Changeset.no_assoc_constraint(:accounts, name: "johan_sports_team_users_user_id_9294a0f7_fk_auth_user_id"), if it was not automatically generated with references when creating the accounts table (it seems not, as the auto-generated name would be "#{table}_#{column}_fkey").