docjazither

docjazither

Hi OGs,
I got a unique_constraint declared in my changeset as follow

defmodule App.Data.User do
use Ecto.schema
import Ecto.Changeset

schema "users" do
field(:full_name, :string)
field(:email, :string)
field(:username, :string)
field(:password, :string)
end

@required_fields []
@optional_fields[
:fullname,
:email,
:username,
:password]

def changeset(struct, params \\ :empty) do
  struct
  |> cast(params, @required_fields ++ @optional_fields)
  |> StringValidator.validate()
  |>validate_required(@required_fields)
  |> unique_constraint(:email)
end

My migration:

defmodule App.Data.Repo.Migrations.CreateUsers do
 use Ecto.Migration
 
 def change do 
 create table(:users) do
      add(:full_name, :string)
      add(:email, :string)
    end

    create(unique_index(:users, :email))

    create(index(:users, :full_name))
  end
end

My controller :

def create(attrs \\ %{}, status_code \\ "approached", opts \\ [sync: true]) do
    with {:ok, user} <-
           status_code
           |> user_with_status()
           |> User.changeset(attrs)
           |> Repo.insert(),

      insert_or_update_subscriber(user, opts)
      {:ok, user}
    end
  end

But I still can create a new user with the same email, that would make a duplicate entry
Any thoughts ?
Thanks

Showing Posts 1 to 10

joseph-lozano

joseph-lozano

@required_fields []

You aren’t passing :email into cast, so you are creating entries in your database that don’t have an email at all, which is why the unique_constraint is not triggering.

malloryerik

malloryerik

I’ve also forgotten to simply pass fields into cast so many times… :exploding_head:

docjazither

docjazither OP

Hi @joseph-lozano
Thanks for the reply, I tried and it still doesn’t work,
I passed it into cast and re-ran ecto.migrations as well.

f0rest8

f0rest8

Just for clarity, is it not working in that you’re expecting to see an error returned in the changeset and not be able to submit the form? Or you’ve checked the actual database and seen that duplicate emails are being saved?

I’m not sure if I’m asking clearly, but I ask because unsafe_validate_unique/4 can be used to quickly return an invalid changeset error for UI purposes, and then you follow it up with a call to unique_constraint/3 to ensure the constraint at the database level.

Another thing to check, is that you are calling Ecto.Repo.insert/2 or Ecto.Repo.update/2 in your Users.create function from your Users.create(user_params) call.

docjazither

docjazither OP

Hi @f0rest8 ,
Yes, I checked the postgres database and I’ve seen that duplicate emails are saved there.
I also tried your suggestion that to call Ecto.Repo.Insert/2, and it did insert it with the same email.

joseph-lozano

joseph-lozano

It might be that you added the unique_index to the migration after they had already run. Have you ensured that you have rolled-back and then re-run the migrations?

csadewa

csadewa

Second this, it seems the problem is actually unique index is not created on DB when it should be created. That’s why DB allow duplicate email. For me, this occasionally happen when i was accidentally run ecto migration even when i haven’t finish written migration file (solution: rollback and migrate, though sometime you need to adjust the migration file to previous version first before rollback), or i just forgot to run the migration

03juan

03juan

You’ll get a postgres unique_violation error when trying to add a unique index on a column that already contains duplicated values, and will even tell you which value it failed on:

08:42:10.891 [info] == Running 20220321064118 GenericNew.Repo.Migrations.MakeUserNameUniqueIndex.change/0 forward

08:42:10.893 [info] create index users_name_index
** (Postgrex.Error) ERROR 23505 (unique_violation) could not create unique index “users_name_index”

table: users
constraint: users_name_index

Key (name)=(test) is duplicated.

03juan

03juan

Are you sure it’s allowing duplicates when testing with non-NULL emails? Because according to psql docs PostgreSQL: Documentation: 18: 5.5. Constraints

In general, a unique constraint is violated if there is more than one row in the table where the values of all of the columns included in the constraint are equal. However, two null values are never considered equal in this comparison. That means even in the presence of a unique constraint it is possible to store duplicate rows that contain a null value in at least one of the constrained columns.

dimitarvp

dimitarvp

As a starting point, do try to insert two records with identical emails from inside psql and see where that gets you.

If you can’t, make sure you have undone the migration that adds the index and then re-run it.

Where Next? Top

Trending in Questions Top

Blokh
Hey guys, I’ve got a huge CSV ( around 10 GB ) that needs to be processed hourly Do you guys have any suggestions what is the best prac...
New
kszambelanczyk
Hello! Could someone please give me a help/sample code, how to delete a file from s3 using waffle/waffle_ecto from Phoenix app. I creat...
New
RemyXRenard
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
velrest
So my question is quite simple and i have found no conclusive answer on forum, google or AI. Should we use :erlang.float for Integer to ...
New
samoloth
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New
FlyingNoodle
If a change or preparation module uses Ash.Changeset.get_argument/2 or Ash.Query.get_argument/2 (or any of the other get_argument functio...
New
psy-q
I’m trying to set up Emacs with elixir-ls via lsp-mode and credo via Flycheck. This should mostly be preconfigured as Flycheck picks up c...
New

Other Trending Topics Top

mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
mcass19
ExRatatui lets you cook up rich terminal UIs in Elixir, powered by Rust’s ratatui via Rustler NIFs. Build interactive terminal applicatio...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge &amp; Solve. They are GUI (Emerge) and State management (S...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews