elixir - Phoenix/Elixir/Ecto - unique_constraint 不适用于名称选项

标签 elixir phoenix-framework ecto

我预期会发生什么:当尝试插入一个已经存在用户名的角色时,调用 Repo.insert 返回给我详细信息错误的变更集。

发生了什么 Ecto 引发了一个硬性异常

我的 Phoenix 项目出现以下错误。

** (exit) an exception was raised:
    ** (Ecto.ConstraintError) constraint error when attempting to insert struct:

    * personas_username_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.

所以我听从了它的建议,在我的数据库中添加了唯一索引的名称,personas_username_index。这个唯一索引肯定存在于真正的底层数据库中。

这是我的架构:

defmodule Poaster.Persona do
  use Ecto.Schema
  import Ecto.Changeset
  alias Poaster.User

  schema "personas" do
    belongs_to :user, User
    field :background_image_url, :string
    field :bio, :string
    field :name, :string
    field :profile_image_url, :string
    field :username, :string

    timestamps()
  end

  @doc false
  def changeset(persona, attrs) do
    persona
    |> cast(attrs, [:username, :name, :bio, :profile_image_url, :background_image_url, :user])
    |> validate_required([:username])
    |> unique_constraint(:username, name: :personas_username_index)
  end
end

这是我的 Controller 代码:

def create(conn, %{"username" => username}) do
    # First attempt
    # result = Ecto.build_assoc(conn.assigns[:signed_user], :personas, %{ username: username })
    # IO.inspect(result)
    # result = Repo.insert(result)
    # IO.inspect(result)

    user = conn.assigns[:signed_user]
    result = Repo.insert(%Persona{username: username, user: user})


    case result do
      {:ok, persona} ->
        conn
          |> put_status(:created)
          |> json(persona_data(persona))

      {:error, _changeset} ->
        conn
          |> put_status(:internal_server_error)
          |> json(%{
              success: false,
              error: %{
                detail: "There was an error saving your username!"
              }
            })
    end
  end

  defp persona_data(persona) do
    %{
      id: persona.id,
      background_image_url: persona.background_image_url,
      bio: persona.bio,
      inserted_at: persona.inserted_at,
      name: persona.name,
      profile_image_url: persona.profile_image_url,
      updated_at: persona.updated_at,
      username: persona.username
    }
  end

迁移文件:

defmodule Poaster.Repo.Migrations.CreatePersonas do
  use Ecto.Migration

  def change do
    create table(:personas) do
      add :username, :string, null: false
      add :name, :string
      add :bio, :string
      add :profile_image_url, :string
      add :background_image_url, :string
      add :user_id, references(:users, on_delete: :nothing), null: false

      timestamps()
    end

    create unique_index(:personas, [:username])
    create index(:personas, [:user_id])
  end
end

最佳答案

我认为您应该插入一个变更集,而不是直接插入模式,以便能够捕获错误,如文档中所述:

https://hexdocs.pm/ecto/Ecto.Changeset.html#unique_constraint/3-complex-constraints

您正在 Persona.changeset/2 函数中调用 unique_constraint/3,但您没有在中调用 Persona.changeset/2你的 Controller 代码。

我认为您应该替换以下行:

result = Repo.insert(%Persona{username: username, user: user}

通过:

result = %Persona{}
  |> Persona.changeset(%{username: username, user: user})
  |> Repo.insert()

或者更确切地说,在执行此操作的上下文中定义一个 create_persona/1 函数,并从您的 Controller 调用该函数(至少这是生成器的组织方式)它)。

关于elixir - Phoenix/Elixir/Ecto - unique_constraint 不适用于名称选项,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63403245/

相关文章:

elixir - 通过 : in Elixir Ecto 以编程方式预加载 has_many

elixir - Ecto 去除预紧力

elixir - 如何制作一个以特定速率处理消息的 GenServer? (每n秒)

elixir - 无法通过监护人获取当前用户

elixir - Ecto.Queryable 未针对用户/Phoenix 错误实现

testing - 如何合并elixir伞app的测试报告?

elixir - `virtual: true` 对嵌入式架构有影响吗?

elixir - Phoenix/Elixir - validate_format() 整数失败

elixir - 自定义混合任务未加载测试环境

elixir - 埃克托 : how to define variable default field values in schema?