删除 Ecto 中的关联(多对多)

Posted

技术标签:

【中文标题】删除 Ecto 中的关联(多对多)【英文标题】:Remove association (for many to many) in Ecto 【发布时间】:2016-08-24 15:38:54 【问题描述】:

我有一个具有多对多关联的用户和程序模型。 他们成功创建了关联,因此用户拥有许多程序(反之亦然),但现在我需要删除关联(但不删除用户或程序)

我在 ProgramController 中有一个函数可以像这样关联它们:

# Myapp.ProgramController

def associate_user(conn, _params) do
   %"_csrf_token" => _csrf_token,"_method"=>_method,"_utf8" => _utf8,"id" => id, "user" => %"email" => email = _params
   pid, _ = Integer.parse(id)

   user = Repo.get_by(User, email: email) |> Repo.preload(:programs)
   program = Repo.get_by(Program, id: pid) |> Repo.preload(:users)

   pid_list = user.programs
   |> Enum.map(fn x -> x.id end)
   |> List.insert_at(0, program.id)

   changeset_list = from(program in Myapp.Program, where: program.id in ^pid_list)
   |> Repo.all
   |> Enum.map(&Ecto.Changeset.change/1)

   from(user in User, where: user.email == ^user.email, preload: [:programs, :role])
   |> Repo.one
   |> User.changeset(%)
   |> Ecto.Changeset.put_assoc(:programs, changeset_list)
   |> Repo.update!

   program = conn.assigns[:program]
   changeset = Program.changeset(program)

   conn
   |> put_flash(:info, "Program updated successfully.")
   |> redirect(to: program_path(conn, :edit, program))
 end

我有一个创建连接表的迁移

defmodule Myapp.Repo.Migrations.AssociateUsersAndPrograms do
  use Ecto.Migration

  def change do

    alter table(:users) do
       add :current_program_id, references(:programs, on_delete: :nilify_all)
    end

    create table(:users_programs, primary_key: false) do
      add :user_id, references(:users, on_delete: :delete_all)
      add :program_id, references(:programs, on_delete: :delete_all)
    end

    create index(:users_programs, [:user_id])
  end
end

由于我在 Ecto.Docs 中没有看到 delete_assoc,我该如何解除特定用户与程序的关联?

到目前为止,仍在学习和喜爱 Elixir(和 Phoenix),因此我们将不胜感激!

【问题讨论】:

您能否制作一个与您的联接表关联的 Ecto 模型/模式? defmodule MyApp.UserProgram do ...schema "users_programs" do ... 然后您可以使用 Ecto 的 api 删除具有匹配 user_id 和 program_id 的 UserProgram。 这也将允许您轻松地将元数据添加到关联中(将来可能用户仅在特定日期之前与程序关联,因此您可以将 expires_at 字段添加到 UserProgram 模式并使用更新它外)。 该功能真的只是在 1 个程序和 1 个用户之间添加关联吗?如果是这样,那看起来效率很低。这不应该超过 3 个查询,一个用于获取程序,一个用于获取用户,一个用于在连接表中插入一个条目。我同意 Bruce 关于为连接表创建架构的观点。 @Dogbert 是的,那时我还在学习,现在我意识到它可以做得更有效:) 【参考方案1】:

您可能需要查看Ecto.Schema.ManyToMany 文档。有几个选项(直接从文档中获取并重新格式化):

    :on_delete- 删除父记录时对关联采取的操作。 :nothing(默认) :delete_all- 只会从连接源中删除数据,不会删除关联的记录。注意 :on_delete 也可以在创建引用时在迁移中设置。如果支持,则首选通过迁移依赖数据库。 :nilify_all:delete_all 不会级联到子记录,除非通过数据库迁移设置。

【讨论】:

与我相关的是删除数据部分:hexdocs.pm/ecto/Ecto.Schema.html#many_to_many/3-removing-data【参考方案2】:

查看Ecto's documentation 的 on_replace 属性。 您只需使用 on_replace 属性标记该字段并像您一直在做的那样调用 Ecto.Changeset.put_assoc,如下所示:

  schema "users" do
    ...
    many_to_many :programs, Program, join_through: "users_programs", on_replace: :delete
    ...
end

文档上不是很清楚。

【讨论】:

以上是关于删除 Ecto 中的关联(多对多)的主要内容,如果未能解决你的问题,请参考以下文章

Spring boot - 多对多关联不删除连接表数据

Hibernate-ORM:12.Hibernate中的多对多关联关系

C# 中的类关联(多对多关系)

关联映射 ---- Hibernate之多对多关系

一口一口吃掉Hibernate——多对多关联映射

关于symfony的多对多关联关系的字段更新