I have a changeset with a unique constraint on one of the fields:
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
In one of the controllers, I insert a new shop. However, if a duplicate shop is being created, an exception is raised:
** (exit) an exception was raised:
** (Ecto.ConstraintError) constraint error when attempting to insert model:
* unique: shops_shop_index
Here is the code where I save the record:
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
Note that %Shop is a struct, while the variable shop is just a value from a query string parameter.
Although I could create a plug for Ecto.ConstraintError, I feel this won't give the the granular control I need for detailed user feedback.
What is a good way to catch the exception and notify the user that the shop has already been registered?
答案 0 :(得分:3)
如果您希望该函数中定义的验证和unique_constraint具有任何结构,则需要将Ecto.Changeset
返回的Shop.changeset/2
直接传递给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