Acces to attrs in create method

Hello,

I want to acces to attrs field in my code to get the username and the email.
I actually want to get my username and email with that way :

def create_users(attrs \\ %{}) do
    %Users{}
    |> Repo.exists?(from u in Users, where: u.username != ^attrs.username and u.email != ^attrs.email)
    |> Users.changeset(attrs)
    |> Repo.insert()
  end

can you help me ?

Hello and welcome,

It’s better to use a constraint for this, it avoids also race condition between exists and insert.

concerning attrs access, are You sure it will be atom keys? Usually it’s not, so You cannot use attrs.something.

Add something in your changeset like…

    |> unique_constraint(:name, message: "Name already taken")
    |> unique_constraint(:email, message: "Email already taken")
1 Like

Actually i’m using unique_constraint like that :

    |> unique_constraint(:email, message: "Name already taken", name: :index_users)
    |> unique_constraint(:username, message: "Name already taken", name: :index_users)

I’ve created an unique index. But when i’m posting an user, I can’t post with sames json but if i just change the username, i can post a new user (with the same email).

In my postman :
this will work the first time :

{
    "users":{
        "email":"toto.toto@gmail.com",
        "password":"azerty123",
        "username":"toto",
        "role":"Manager",
        "team":1
    }
}

the second time it will return an error which is normal.
But this :

{
    "users":{
        "email":"toto.toto@gmail.com",
        "password":"azerty123",
        "username":"toto2",
        "role":"Manager",
        "team":1
    }
}

Will work. The problem is that there is a same email and i don’t want to create an user with the same email or username.
In fact the second json should return an error but actually it’s not.

You are using the same index :index_users for both the email and username uniqueness checks. You need to have a separate unique index for email.

I’ve created an other unique index as follow :

defmodule Gotham.Repo.Migrations.EmailIndex do
  use Ecto.Migration

  def up do
    create unique_index(:users, [:email, :username,:password], name: :index_email)
  end

  def down do
    drop index(:users, [:users, :username,:password], name: :index_email)
  end
end

my user_index looks like :

defmodule Gotham.Repo.Migrations.UserIndex do
  use Ecto.Migration

  def up do
    create unique_index(:users, [:username, :email], name: :index_users)
  end

  def down do
    drop index(:users, [:username, :email], name: :index_users)
  end
end

But in my users.ex, i have an error :

Request: POST /api/users
** (exit) an exception was raised:
    ** (Ecto.ConstraintError) constraint error when attempting to insert struct:

    * index_users (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 defined the following constraints:

    * index_email (unique_constraint)

I don’t know why i have this error because i’ve created the index_email.

My users.ex changeset looks like :

def changeset(users, attrs) do
    users
    |> cast(attrs, [:username, :email,:password, :chartType, :colorChart, :period, :role, :team])
    |> validate_required([:username, :email, :password])
    |> validate_format(:email, ~r/[a-z0-9]+@[a-z0-9]+\.[a-z]{2,4}/)
    |> unique_constraint(:email, message: "email already taken", name: :index_email)

Can you enlighten me ?

This will create an index where the triplet email-username-password is unique. But not any of them individually. For example you could create user Nicd with email nicd@example.com and password “foo”, and also user Nicd with email nicd@example.com and password “bar”.

Also, you really don’t want to make passwords unique. Otherwise people will know what other passwords are in use.

This will create a unique index where the username-email pair is unique. Same as above, you could create user Nicd with email nicd@example.com and user Nicd with email anotheremail@example.com. This is probably not what you want.

You need to think about what the uniqueness conditions are that you want. From your posts I think you want both username and email to be unique individually. For that you need to create a unique index just for email and another just for username.

This error is because you removed the |> unique_constraint(:username, message: "Name already taken", name: :index_users) from your changeset code. You need to have them both there, referring to the different indexes.

Yeah it works ! Thank a lot :slight_smile: