Error: constraint error when attempting to insert struct:

Hello. I Have an error above: I’m don’t able to detect a solution, please help me!

THE ERROR: :frowning:

Ecto.ConstraintError at POST /api/users

constraint error when attempting to insert struct:

  • users_email_index (unique_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 unique_constraint/3 on your changeset with the constraint :name as an option.

The changeset has not defined any constraint.

MY USER MODULE:
defmodule PGestor.User do
use Ecto.Schema
import Ecto.Changeset

@primary_key {:id, Ecto.UUID, autogenerate: true}

schema “users” do
field :name, :string
field :email, :string
field :password_hash, :string
field :password, :string, virtual: true
field :avatar, :string
timestamps()
end

@cast_params [:name, :email, :password, :avatar]
@required_params [:name, :email, :password]

def build(params) do
params
|> changeset()
|> apply_action(:insert)
end

def changeset(params), do: create_changeset(%MODULE{}, params)
def changeset(user, params), do: create_changeset(user, params)

defp create_changeset(module_or_user, params) do
module_or_user
|> cast(params, @cast_params)
|> validate_required(@required_params)
|> unique_constraint(:email, name: :users_email_index)
|> validate_format(:email, ~r/@/)
|> validate_length(:password, min: 6)
|> put_pass_hash()
end

defp put_pass_hash(%Ecto.Changeset{valid?: true, changes: %{password: password}} = changeset) do
change(changeset, Argon2.add_hash(password))
end

defp put_pass_hash(changeset), do: changeset
end

MY CONTROLLER THAT CALL CREATE CHANGESET
def create(conn, params) do
with {:ok, user} ← PGestor.create_user(params),
{:ok, token, _claims} ← Guardian.encode_and_sign(user) do
conn
|> put_status(:created)
|> render(“create.json”, %{user: user, token: token})
end
end
MY CALL TO ACTION

defmodule PGestor.User.Create do
alias PGestor.{Repo, User}

def call(params) do
params
|> User.build()
|> create_user()
end

defp create_user({:ok, struct}), do: Repo.insert(struct)
defp create_user({:error, _changeset} = error), do: error
end

MY MIGRATION USER

def change do
create table(:users, primary_key: false) do
add :id, :uuid, primary_key: true
add :name, :string
add :email, :string
add :password_hash, :string
add :avatar, :string

    timestamps()
 end

 create unique_index(:users, [:email])

end

(NOTE: triple-backticks ``` will preserve the formatting of code and make it easier to read)

This is unusual; apply_action will result in a User struct, which loses things like unique_constraint that add information to the changeset about how to handle errors from the SQL INSERT.

The code is not shown, but my hypothesis is that PGestor.create_user calls User.build and then tries to pass the resulting struct to Repo.insert. That would produce exactly the error message shown when trying to insert a user with an email that’s already taken.

The fix in that case would be to skip apply_action and pass the changeset to Repo.insert directly; that will allow unique_constraint to operate as expected.

Sorry to forgot de ``` , thank you for advertising me.
A did as you recommended and works well.

defmodule PGestor.User.Create do
  alias PGestor.{Repo, User}

  def call(params) do
    params
    |> User.changeset()
    |> Repo.insert()
  end
end
` ``
thanks a lot!