I’ve seen this pattern used often in the Ecto documentation, Phoenix controller templates, and numerous other places. It’s not clear to me why it is often used:
The pattern is:
user = MyRepo.get!(User, id)
{:ok, _user} = MyRepo.delete(user)
There are a few variations on this pattern, some would use MyRepo.delete!/1 with a bang instead, but all of them retrieved a record and then deleted it. In most situations the user value is discarded and never used again after the delete!/1 call.
Why is it common practice to retrieve a record prior to deleting it? In most of the places I’ve seen this code used, the code that invokes the Ecto queries is not wrapped in a transaction, so it doesn’t guard against the scenario where another process/client deletes the same record between the get and the delete. It will be possible for the code to try to delete record that is already gone and raise a Ecto.StaleEntryError (which Phoenix turns into 409 if you are using the phoenix_ecto package).
One answer this question would be that Ecto doesn’t provide a function that makes it easy to delete a record by ID. Why does Ecto have a function for retrieving a record by ID, but not one for deleting a record by ID? For example, Ecto provides:
MyRepo.get!(User, 42)
But in order to delete a record by ID I have to do this:
MyRepo.delete_all(from(u in User, where: u.id == ^id))
Or this (hacky):
MyRepo.delete(%User{id: id})
Is there a reason we can’t have a nice Repo.delete!(queryable :: Ecto.Queryable.t(), id :: term(), Keyword.t()) :: Ecto.Schema.t() callback?
Places where I see this pattern used:
- Ecto docs - Ecto.Repo — Ecto v3.14.0
- Phoenix templates - https://github.com/phoenixframework/phoenix/blob/master/priv/templates/phx.gen.html/controller.ex
Related: Ecto delete a record WITHOUT selecting first - #8 by fireproofsocks






















