【问题标题】:Ecto Reload belongs_to Association After Successful Repo.update成功 Repo.update 后 Ecto Reload belongs_to 关联
【发布时间】:2016-01-22 11:21:23
【问题描述】:

在参数中通过parent_id 将给定child 的关联从parent_a 更改为parent_b 会留下陈旧的record.parent 对象。

例如(假设参数匹配%{child: %{id: '1', parent_id: '6'}}

# ...
child = Repo.get(Child, child_id)
|> preload([:parent])
changeset = Child.changeset(child, child_params)

case Repo.update(changeset) do
  {:ok, child} ->
    IO.puts child.parent_id # returns '6', or the new, changed `id`
    IO.puts child.parent.id # returns '5', or the old id
                            # child.parent is stale
# ...

更新后检索新关联的父记录的正确方法是什么?

【问题讨论】:

    标签: elixir phoenix-framework ecto


    【解决方案1】:

    强制预加载。默认情况下,Ecto 不会预加载已加载的关联。

    child
    |> Child.changeset(params)
    |> Repo.update!()
    |> Repo.preload(:parent, force: true)
    

    如果您想以不同的方式处理错误,也可以不使用 bang 更新

    child
    |> Child.changeset(params)
    |> Repo.update()
    |> case do
      {:ok, child} -> {:ok, Repo.preload(child, :parent, force: true)}
      error -> error
    end
    

    在一个更现实的错误处理示例中,它可能看起来像

    with {:ok, child} <- get_child(child_id),
         {:ok, child} <- update_child(child, params) do
      # Do stuff
    else
      {:error, %Ecto.Changeset{} = changeset} -> # Handle error
      {:error, reason} -> # Handle error
    end
    
    defp get_child(child_id) do
      case Repo.get(Child, child_id) do
        nil -> {:error, :not_found}
        child -> {:ok, child}
      end  
    end
    
    defp update_child(child, params) do
      updated_child = 
        child
        |> Child.changeset(params)
        |> Repo.update!()
        |> Repo.preload(:parent, force: true)
    rescue
      error in Ecto.InvalidChangesetError -> {:error, error.changeset}
      error in RuntimeError -> {:error, error.message}
    end
    

    【讨论】:

      【解决方案2】:

      目前在 Ecto 中没有内置的方法来执行此操作。您还有无法使用预加载的问题,因为关联已被预加载。

      一个选项是这样的:

      %{child | parent: Repo.get!(Parent, child.parent_id)}
      

      您也可以选择在调用 Repo.update 之前不调用 preload,这将阻止已加载关联。

      【讨论】:

      • 使用 Ecto 2.0,您现在可以将 force: true 传递给 Repo.preload 以强制重新加载已加载的关联。来源:hexdocs.pm/ecto/2.0.0-rc.1/Ecto.Repo.html#c:preload/3
      • @JonathanPerret 值得将您的评论变成答案吗?在现代版本的 Ecto 中,这似乎是解决问题的惯用方式。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-25
      • 1970-01-01
      • 1970-01-01
      • 2016-12-30
      • 1970-01-01
      • 2017-11-29
      相关资源
      最近更新 更多