【问题标题】:How to make forms and transactions play well in phoenix + ecto?如何让表单和事务在phoenix + ecto 中发挥得很好?
【发布时间】:2017-03-08 10:57:43
【问题描述】:

我正在玩 Phoenix + Ecto,我偶然发现了一些我觉得不习惯的东西。

我有一个代表Invitation 的表单。创建邀请时,我们还需要创建User,显然我希望两者都发生在事务中,因此我保持数据一致性。在我的表格中,我要求nameemail

由于我希望我的视图中的 Invitation 变更集能够正确表示错误,所以我最终得到了这段代码......但看起来不太好。

您知道在 Phoenix + Ecto 中执行此操作的更好方法吗?

def create(params) do
  Repo.transaction(fn ->
    case Repo.insert(User.email_changeset(%User{}, params)) do
      {:ok, user} ->
        changeset = Invitation.changeset(%Invitation{}, params)
        case Repo.insert(Ecto.Changeset.change(changeset, user_id: user.id)) do
          {:ok, user} ->
            user
          {:error, changeset} ->
            Repo.rollback(changeset)
        end
      {:error, _changeset} ->
        Repo.rollback(%{Ecto.Changeset.add_error(changeset, :email, "Wrong email") | action: :insert})
    end
  end)
end

【问题讨论】:

    标签: elixir phoenix-framework ecto


    【解决方案1】:

    您正在寻找with 运算符。这种语法的美妙之处在于,如果在任何时候你没有得到你所期望的,它会停止命令链并触发你的 else 块:

    Repo.transaction(fn ->
      with {:ok, first_object} <- create_some_object,
           {:ok, second_object} <- create_another(first_object.something)  do
           second_object
      else
        {:error, error_key} ->
          Repo.rollback(error_key)
      end
    end)
    

    如果 create_some_object 没有返回匹配 {:ok, first_object} 的结构,则永远不会创建 second_object。很酷,对吧?

    【讨论】:

      【解决方案2】:

      你可以试试Ecto.Multi。这是一个例子:

      defmodule Service do
        alias Ecto.Multi
        import Ecto
      
        def insert_changeset(params) do
          Multi.new
          |> Multi.insert(:user, User.email_changeset(%User{}, params))
          |> Multi.insert(:invitation, Invitation.changeset(%Invitation{}, params))
        end
      end
      

      还有你的创建功能:

      def create(params) do
        Service.insert_changeset(params)
        |> Repo.transaction
      end
      

      或者您可以进行模式匹配以使您的代码更好

        def create(params) do
          Repo.transaction(fn ->
            changeset = User.email_changeset(%User{}, params)
            changeset
            |> Repo.insert
            |> invitation_insert(params)
          end)
        end
      
        defp invitation_insert({:error, changeset}, _params), do: Repo.rollback(changeset)
        defp invitation_insert({:ok, _}, params) do
          Invitation.changeset(%Invitation{}, params)
          |> Repo.insert
          |> do_invitation_insert
        end
      
        defp do_invitation_insert({:ok, user}), do: user
        defp do_invitation_insert({:error, changeset}), do: Repo.rollback(changeset)
      

      【讨论】:

      • 我曾想过使用Multi,但在我的情况下,我需要先创建user,因为我需要user_id 作为我的邀请函。看起来Multi 用于“并行”操作。模式匹配版本看起来确实好多了!
      • defp do_invitation_insert({:error, changeset}), do: Repo.rollback(changeset) 是否也会回滚 User
      • @Omin 是的,它仍然在同一个事务中
      • 不过,如何将 Ecto.Multi 中的错误返回到表单中?
      猜你喜欢
      • 2011-02-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-27
      • 2014-03-18
      • 2014-05-15
      相关资源
      最近更新 更多