【问题标题】:Handling particular constraint errors in Phoenix处理 Phoenix 中的特定约束错误
【发布时间】:2016-08-30 04:26:34
【问题描述】:

我有一个变更集,其中一个字段具有唯一约束:

defmodule Oauth.Shop do
  use Ecto.Model
  import Ecto.Changeset
  alias Ecto.Changeset

  schema "shops" do
    field :shop, :string
    field :access_token, :string
    field :scope, :string
    field :active, :boolean
    timestamps
  end

  def changeset(shop, params \\ %{}) do
    shop
    |> cast(params, [:shop, :access_token, :scope, :active])
    |> Changeset.validate_required([:shop, :access_token, :scope, :active])
    |> unique_constraint(:shop)
  end
end

在其中一个控制器中,我插入了一个新商店。但是,如果正在创建重复商店,则会引发异常:

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

    * unique: shops_shop_index

这是我保存记录的代码:

  def save_shop({:ok, access_params}, shop) do
    Repo.insert(%Shop{shop: shop, access_token: access_params.access_token, scope: access_params.scope})
    hook_uninstall(shop, access_params.access_token)
    {:ok}
  end

请注意,%Shop 是一个结构,而变量 shop 只是来自查询字符串参数的值。

虽然我可以为 Ecto.ConstraintError 创建一个插件,但我觉得这无法提供我需要的详细用户反馈的精细控制。

有什么好的方法可以捕获异常并通知用户商店已经注册?

【问题讨论】:

  • 你是如何插入数据库的?您使用的是Repo.insert! 还是Repo.insert?因为根据docs,后者应该返回一个两个元组{:error, changeset},您可以轻松地对其进行模式匹配
  • 您确定将Changeset 传递给Repo.insert 而不是直接传递结构吗?
  • @Dogbert 等人。 . .更新了问题以包含 Repo.insert()。

标签: elixir phoenix-framework


【解决方案1】:

如果您希望该函数中定义的验证和 unique_constraint 具有任何效果,则需要将 Shop.changeset/2 返回的 Ecto.Changeset 直接传递给 Repo.insert 而不是 Shop 结构。您还必须使用模式匹配来处理错误情况。

def save_shop({:ok, access_params}, shop) do
  case Repo.insert(Shop.changeset(%Shop{}, %{shop: shop, access_token: access_params.access_token, scope: access_params.scope})) do
    {:ok, _} ->
      hook_uninstall(shop, access_params.access_token)
      {:ok}
    {:error, changeset} ->
      # Put whatever value you want to return in case of an error here.
      # You can get more details about the error using `changeset.errors`.
      {:error}
  end
end

(我假设您只想在插入成功时运行hook_uninstall。如果不是这种情况,您可以将该行复制到另一个分支或将其移到case之外。)

在可能的情况下使用模式匹配而不是使用.field 访问地图的字段也被认为更惯用:

def save_shop({:ok, %{access_token: access_token, scope: scope}}, shop) do
  case Repo.insert(Shop.changeset(%Shop{}, %{shop: shop, access_token: access_token, scope: scope})) do
    {:ok, _} ->
      hook_uninstall(shop, access_token)
      {:ok}
    {:error, changeset} ->
      # Put whatever value you want to return in case of an error here.
      # You can get more details about the error using `changeset.errors`.
      {:error}
  end
end

【讨论】:

  • 我知道这是如何工作的,谢谢。第二个例子中的错字,第二行:缺少右括号。感谢您帮助我找到解决方案和惯用的实现方式。我仍在打破一些旧习惯,需要提醒我模式匹配。
  • 修正错字,谢谢。很高兴有帮助!模式匹配可能需要一些时间来适应,但是一旦你很好地理解了它,每次你不得不使用一种没有模式匹配的语言时,你就会错过它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-01
  • 2019-08-31
  • 2014-11-02
  • 1970-01-01
  • 1970-01-01
  • 2016-03-04
相关资源
最近更新 更多