Ограничение уникального индекса не работает в приложении Phoenix

В моем приложении Phoenix у меня есть модель пользователя следующим образом:

defmodule MyApp.User do
  use MyApp.Web, :model

  schema "users" do
    field :username, :string, unique: true
    field :email, :string, unique: true
    field :crypted_password, :string
    field :password, :string, virtual: true

    timestamps
  end

  @required_fields ~w(email password username)
  @optional_fields ~w()

  @doc """
  Creates a changeset based on the `model` and `params`.

  If no params are provided, an invalid changeset is returned
  with no validation performed.
  """
  def changeset(model, params \\ :empty) do
    model
    |> cast(params, @required_fields, @optional_fields)
    |> unique_constraint(:email)
    |> unique_constraint(:username)
    |> validate_format(:email, ~r/@/)
    |> validate_length(:password, min: 5)
  end
end

У меня также есть следующая миграция:

defmodule MyApp.Repo.Migrations.CreateUser do
  use Ecto.Migration

  def change do
    create table(:users) do
      add :email, :string
      add :username, :string
      add :crypted_password, :string

      timestamps
    end

    create unique_index(:users, [:email])
    create unique_index(:users, [:username])
  end
end

И у меня есть registration_controller_ex следующим образом:

defmodule MyApp.RegistrationController do
  use MyApp.Web, :controller
  alias MyApp.User

  def new(conn, _params) do
    changeset = User.changeset(%User{})
    render conn, changeset: changeset
  end

  def create(conn, %{"user" => user_params}) do
    changeset = User.changeset(%User{}, user_params)

    if changeset.valid? do
      user = MyApp.Registration.create(changeset, MyApp.Repo)
      conn
      |> put_flash(:info, "Your account was created")
      |> redirect(to: "/")
    else
      conn
      |> put_flash(:info, "Unable to create account")
      |> render("new.html", changeset: changeset)
    end
  end
end

Итак, при всем этом я почти уверен, что мои поля username и email в User являются уникальными индексами. Я также убеждаюсь, что они уникальны, вызывая unique_constraint для проверки User.changeset. Однако, когда в моем интерфейсе я создаю пользователя с тем же адресом электронной почты и именем пользователя, что и ранее созданный, набор изменений проверяется, и пользователь «создается». (на самом деле он не создан, когда я смотрю в базу данных, ничего не добавляется)

Я получаю следующее в журналах своего сервера, но мой changeset.valid? правда.

[debug] BEGIN [] OK query=139.3ms queue=8.2ms
[debug] INSERT INTO "users" ("crypted_password", "email", "inserted_at", "updated_at", "username") VALUES ($1, $2, $3, $4, $5) RETURNING "id" ["$2b$12$MN1YxFUGLMIJYXseZn0sjuuVs9U1jRdYtRr9D8XQsAqdh.D2sRRXa", "[email protected]", {{2015, 9, 30}, {11, 7, 25, 0}}, {{2015, 9, 30}, {11, 7, 25, 0}}, "username"] ERROR query=5.5ms
[debug] ROLLBACK [] OK query=0.4ms

Кроме того, другие вещи, которые я ищу в своей функции User.changeset (например, минимальная длина пароля и другие вещи), сообщаются пользователю и работают нормально. Не сообщаются только уникальные индексы для :email и :username.


person David Gomes    schedule 30.09.2015    source источник


Ответы (1)


unique_constraint будет проверяться базой данных и, таким образом, срабатывает только при вставке записи. Вызов changeset.valid? не будет проверять ограничения и, таким образом, в этом случае вернет true. Вам нужно проверить возвращаемый кортеж вставки репо и действовать следующим образом:

def create(conn, %{"user" => user_params}) do
  changeset = User.changeset(%User{}, user_params)
  case  MyApp.Repo.insert changeset do
    {:ok, changeset} -> 
      conn
      |> put_flash(:info, "Your account was created")
      |> redirect(to: "/")      
    {:error, changeset} ->
      conn
      |> put_flash(:info, "Unable to create account")
      |> render("new.html", changeset: changeset)      
  end
end

теперь ваш changeset.errors обогащен, и вы сможете получить ошибку с помощью changeset.errors[:email]

person Gerard de Brieder    schedule 30.09.2015
comment
Это не сработало. У меня проблема: Registration_controller.ex:15: function changeset/0 undefined - person Cuong Ta; 17.12.2015
comment
Для потомства строку выше следует немного изменить: changset = User.changeset(%User{}, user_params) - person pip; 12.04.2016