2016-08-30 136 views
2

特定约束错误,我有对中的一个字段唯一约束的变更:处理在凤凰

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只是查询字符串参数中的一个值。

尽管我可以为Ecto.ConstraintError创建一个插件,但我觉得这不会给我详细的用户反馈所需的精细控制。

什么是捕捉异常并通知用户该商店已被注册的好方法?

+1

你是如何插入数据库的?你在使用'Repo.insert!'还是'Repo.insert'?因为根据[docs](https://hexdocs.pm/ecto/Ecto.Changeset.html#unique_constraint/3),后者应该返回一个两个元组'':error,changeset}',您可以轻松地匹配模式匹配在 –

+0

你确定你传递'Changeset'到'Repo.insert'而不是直接结构? – Dogbert

+0

@Dogbert et al。 。 。更新了问题以包含Repo.insert()。 – sheldonkreger

回答

2

您需要通过Ecto.Changeset通过Shop.changeset/2Repo.insert而不是Shop结构直接,如果你想在函数定义有任何影响的验证和unique_constraint返回。您还必须使用模式匹配来处理错误情况。

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来访问Map的字段:

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 
+0

我明白这是如何工作的,谢谢。第二个例子中的错字,第二行:缺少右括号。感谢您帮助我看到解决方案和惯用实施。我仍然打破一些旧习惯,需要提醒模式匹配。 – sheldonkreger

+1

修正了错字,谢谢。很高兴帮助!模式匹配可能需要一些时间才能习惯,但是一旦你理解得很清楚,每次必须使用没有模式匹配的语言时,都会错过它。 – Dogbert