bao*_*ist 8 elixir ecto phoenix-framework
将params 中给定的关联child从 from更改parent_a为parent_bvia 会parent_id留下一个陈旧的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
# ...
Run Code Online (Sandbox Code Playgroud)
更新后检索新关联的父记录的正确方法是什么?
强制预加载。默认情况下,Ecto 不会预加载已经加载的关联。
child
|> Child.changeset(params)
|> Repo.update!()
|> Repo.preload(:parent, force: true)
Run Code Online (Sandbox Code Playgroud)
如果你想以不同的方式处理错误,或者没有 bang 更新
child
|> Child.changeset(params)
|> Repo.update()
|> case do
  {:ok, child} -> {:ok, Repo.preload(child, :parent, force: true)}
  error -> error
end
Run Code Online (Sandbox Code Playgroud)
在一个更现实的错误处理示例中,它可能看起来像
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
Run Code Online (Sandbox Code Playgroud)
        目前 Ecto 中没有内置方法可以执行此操作。您还存在无法使用预加载的问题,因为关联已经预加载。
一种选择是这样的:
%{child | parent: Repo.get!(Parent, child.parent_id)}
Run Code Online (Sandbox Code Playgroud)
您还可以选择在调用之前不调用预加载Repo.update,这将阻止关联已被加载。