与ECTO和put_assoc/4进行多对多 [英] Many-to-Many with ECTO and put_assoc/4

查看:109
本文介绍了与ECTO和put_assoc/4进行多对多的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I try associate 2 existing Many-to-Many records with ECTO and put_assoc/4 but won't remove elements when try update.

基本上,我有项目和用户.为了管理用户对项目的访问,我具有表"user_project".

Basically i have projects and users . for manage the access of users to projects i have the table "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

当我创建带有项目列表的用户时,所有内容都会创建,但是当尝试更新用户项目时,删除对一个项目的访问权限就不会发生...

When i create user with list of projects is everything created but when try update the user projects removing the access to one project nothing happens ...

例如:

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只添加新元素却不删除?

Why? Why put_assoc/4 just add new elements don't remove?

推荐答案

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

@IvanYurov are right . You need to define on_replace option but also need the preloads of user updated (in every request). Basiclly for your test to be correct you need to do a user=get_user (with the preload of the projects) before Management.upsert_user_projects(user, ...).

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

这篇关于与ECTO和put_assoc/4进行多对多的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆