如何使用 Ecto 更新多对多关联

How to update a many-to-many association with Ecto

我有一个 Phoenix 应用程序,其中包含两个具有多对多关系的资源。为简化起见,我们称它们为 post 和标签。当我从头开始创建 post 时,我可以将现有标签与其相关联。我还可以编辑一个没有标签的 post 来关联标签。但是,每当我想编辑与 post 关联的标签时,我都会收到此错误:

you are attempting to change relation :tags of MyApp.Posts.Post but the `:on_replace` option of
this relation is set to `:raise`

这是我的变更集 - 我使用的 put_assoc 有 4 个参数,但根据文档,未使用 opts 一个,所以我无法设置 on_replace 选项:

def changeset(%__MODULE__{} = user, attrs) do
    tag_ids = if attrs["tags"], do: attrs["tags"], else: []
    tags = Tags.by_ids(tag_ids)

    user
    |> Repo.preload(:tags)
    |> cast(attrs, [:title, :body])
    |> put_assoc(:tags, tags)
    |> validate_required([:body])
  end

我的想法是只更新两者之间的关联 - 我永远不会从 posts 表单中创建或删除标签本身。我应该以不同的方式更新它们吗?

这里的问题是 ecto 不知道如何处理你直接更新你的用户和标签之间已经存在的关系,它应该删除所有并只插入新的,它应该添加到现有的?

默认情况下,正如错误所说,它会:raise一个错误。

它提到的选项是在关系上声明的,而不是在 casting/putting assocs 时声明的,如记录 here

为了始终使用放置在变更集上的标签,您应该在关系上添加此选项

many_to_many :tags, MyApp.Tag, join_through: "posts_tags", on_replace: :delete

有关此选项的更多信息here