使用 ECTO 和 put_assoc/4 进行多对多

标签 many-to-many elixir phoenix-framework ecto

我尝试将 2 个现有多对多记录与 ECTO 和 put_assoc/4 关联,但在尝试更新时不会删除元素。

基本上我有项目和用户。为了管理用户对项目的访问,我有表“user_project”。

def Project do
    schema "project" do
    ...
    # users (if user_type is :admin)
    many_to_many(
    :users,
    User,
    join_through: "user_project"
    )
    ...
    end
end

def User do
    schema "user" do
    ...
    # users (if user_type is :admin)
    many_to_many(
    :projects,
    User,
    join_through: "user_project"
    )
    ...
    end
    ...
    def changeset_insert_not_active(%User{} = user, attrs) do
            user
            |> cast(attrs, @required_fields)
            |> put_assoc(:projects, attrs.projects)
            |> validate_required(@required_fields)
            |> validate_user_type()
            |> validate_format(:email, ~r/@/)
            |> unique_constraint(:email)
    end
    ...
    def changeset_update_projects(%User{} = user, projects) do
            user
            |> cast(%{}, @required_fields)
            # associate projects to the user
            |> put_assoc(:projects, projects)
    end
    ...
end

def Management do
    ...
    def create_user(attrs \\ %{}, project_ids \\ []) when is_list(project_ids) do

        projects =
        Project
        |> where([project], project.id in ^project_ids)
        |> Repo.all()

        %User{}
        |> User.changeset_insert(attrs 
        |> Map.put(:projects, projects))
        |> Repo.insert()
    end
    ...
    def upsert_user_projects(user, project_ids) when is_list(project_ids) do
        projects =
        Project
        |> where([project], project.id in ^project_ids)
        |> Repo.all()

        user
        |> User.changeset_update_projects(projects)
        |> Repo.update()
    end
    ...
end

当我使用项目列表创建用户时,所有内容都已创建,但是当尝试更新用户项目时,删除对一个项目的访问权限时,什么也没有发生......

例如:

test "xxx" do 
    Management.upsert_user_projects(user, [1])
    l = Management.list_user_project_by_user(user.id)
    IO.puts("---------")
    IO.puts("1 - length:#{length(l)}")
    IO.puts("---------")
    Enum.each(l, fn project ->
        IO.inspect(project.project_id, label: "inserted project_id ")
    end)

    Management.upsert_user_projects(user, [2])
    l = Management.list_user_project_by_user(user.id)

    IO.puts("---------")
    IO.puts("2 - length:#{length(l)}")
    IO.puts("---------")
    Enum.each(l, fn project ->
        IO.inspect(project.project_id, label: "inserted project_id ")
    end)
end

返回值:

---------
1 - length:1
---------
inserted project_id : 1
2 - length:2
---------
inserted project_id : 1
inserted project_id : 2
为什么?为什么 put_assoc/4 只添加新元素而不删除?

最佳答案

@IvanYurov 是对的。您需要定义 on_replace 选项,但还需要更新用户的预加载(在每个请求中)。基本上,为了使测试正确,您需要在 Management.upsert_user_projects(user, ...) 之前执行 user=get_user (预加载项目)。

def get_user(id) do
  Repo.get(User, id)
  |> Repo.preload(:projects)
end

关于使用 ECTO 和 put_assoc/4 进行多对多,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54369243/

相关文章:

elixir - 如何指示 ecto 不创建自动递增 id 字段?

elixir - 创建具有变化状态的惰性序列(流),例如斐波那契数?

testing - Phoenix API : How do I test a POST/create when I don't know the ID of the new resource?

Elixir/Phoenix/Timex : protocol Timex. 协议(protocol)未实现:错误

symfony - 管理中的可排序奏鸣曲类型模型

many-to-many - 有一张人表,我想将他们相互链接,多对多,链接是双向的

php - 如何在跨三个表的 mysql 查询中使用联接

php - Doctrine setter 在多对多关系上有什么用?

elixir - 小写电子邮件地址并在保存前创建 MD5 和

postgresql - 如何在 Phoenix 的 Ecto 查询中按类别选择具有最大日期组的 ID?