Repo.delete Ecto.Changeset.cast/4, when the changeset is nil

Hello, I want to delete my record in my db, when this exists I have no problem, but whenever this isn’t I receive this error:

The following arguments were given to Ecto.Changeset.cast/4:
    
        # 1
        nil
    
        # 2
        %{}
    
        # 3
        [:name, :lastname, :username, :email]
    
        # 4
        []
    
    Attempted function clauses (showing 5 out of 5):
    
        def cast(_data, %{__struct__: _} = params, _permitted, _opts)
        def cast({data, types}, params, permitted, opts) when is_map(data)
        def cast(%Ecto.Changeset{types: nil}, _params, _permitted, _opts)
        def cast(%Ecto.Changeset{changes: changes, data: data, types: types, empty_values: empty_values} = changeset, params, permitted, opts)
        def cast(%{__struct__: module} = data, params, permitted, opts)
    
    (ecto) lib/ecto/changeset.ex:460: Ecto.Changeset.cast/4
    (auth_service) lib/users/user_schema.ex:26: AuthService.Users.UserSchema.changeset/2
    (auth_service) lib/users/user_query.ex:24: AuthService.Users.UserQuery.delete_user/1

my deleter function:

  def delete_user(id) do
    user = find_user_with_user_id(id) |> UserSchema.changeset
    case Repo.delete(user) do
      {:ok, info} -> {:ok, info}
      {:error, changeset} -> {:error, changeset}
    end
  end

  def find_user_with_user_id(id) do
    Repo.get(UserSchema, id)
  end

iex(1)> UserQuery.delete_user("2975346a-cb25-4347-add7-4e14340d1bda")

if I change my function to this:

  def find_user_with_user_id(id) do
    Repo.get!(UserSchema, id)
  end

I have this error:

** (Ecto.NoResultsError) expected at least one result but got none in query:

from u0 in AuthService.Users.UserSchema,
  where: u0.id == ^"2975346a-cb25-4347-add7-4e14340d1bda"

    (ecto) lib/ecto/repo/queryable.ex:76: Ecto.Repo.Queryable.one!/3
    (auth_service) lib/users/user_query.ex:24: AuthService.Users.UserQuery.delete_user/1

how can I fix this and check this by changeset before deleting ?

by the way, even if I don’t use changeset like this:

  def delete_user(id) do
    user = find_user_with_user_id(id)
    #|> UserSchema.changeset
    case Repo.delete(user) do
      {:ok, info} -> {:ok, info}
      {:error, changeset} -> {:error, changeset}
    end
  end

error:

** (FunctionClauseError) no function clause matching in Ecto.Repo.Schema.delete/3    
    
    The following arguments were given to Ecto.Repo.Schema.delete/3:
    
        # 1
        AuthService.Repo
    
        # 2
        nil
    
        # 3
        []
    
    Attempted function clauses (showing 2 out of 2):
    
        def delete(name, %Ecto.Changeset{} = changeset, opts) when is_list(opts)
        def delete(name, %{__struct__: _} = struct, opts) when is_list(opts)
    
    (ecto) lib/ecto/repo/schema.ex:408: Ecto.Repo.Schema.delete/3
    (auth_service) lib/users/user_query.ex:26: AuthService.Users.UserQuery.delete_user/1

I can fix it with this way but I think it’s not good

def delete_user(id) do
    user = find_user_with_user_id(id)
    if user != nil do
      Repo.delete(user)
    else
      {:error, "User dosent exist"}
    end
  end

  def find_user_with_user_id(id) do
    Repo.get(UserSchema, id)
  end

I see two ways of handling possible failure, failing fast like this:

id |> Accounts.get_user!() |> Accounts.delete_user!()

This way you get an error if there is no user and if used in controller it would be converted to 404 which is correct when ID of the user is in the URL.

Or you can handle all cases using with and ok / error tuples:

with {:ok, user} <- Accounts.get_user(id) do
      Accounts.delete_user(user)
else
      {:error, msg} -> # handle error

In this case the handling of possible nil should be moved to find_user_with_user_id that would return {:error, “User not found”} tuple. Alternatively you can use guard that checks for nil in with statement.

1 Like

I think I’m forced to use conditions, because I have to check another possible like is_true UUID because my project exists outside of my phoenix and elixir cant handel it then with your help I changed my code like this.

  def delete_user(id) do
    with {:ok, user_id} <- Ecto.UUID.cast(id), {:ok, user} <- find_user_with_user_id(user_id) do
      Repo.delete(user)
    else
      _ ->
      {:error, "User dosent exist"}
    end
  end

I expected the changeset handles it and I don’t need to use any control flow.