Elixir ecto 2 create many_to_many association

后端 未结 2 569
后悔当初
后悔当初 2020-12-16 15:52

How can I make a many to many relation with ecto 2? As an example app I want to create a Post which can be in multiple categories. The categories already exist. For example

2条回答
  •  我在风中等你
    2020-12-16 16:13

    After a good night of sleep and some digging in the ecto unit tests i have found a partial answer. The right function to call is Ecto.Changeset.put_assoc. It returns a changeset. The rest of the question is on the bottom of this reply.

    def run_insert_1 do
      c1 = Repo.get!(Category, 1)
      c2 = %Category{name: "cat 2"}
    
      # Inserting
      changeset =
        %Post{title: "1"}
        |> Ecto.Changeset.change
        |> Ecto.Changeset.put_assoc(:categories, [c1, c2])
      post = Repo.insert!(changeset)
      IO.inspect post
    end
    
    def run_insert_2 do
      c1 = Repo.insert! %Category{name: "cat 1"}
      c2 = %Category{name: "cat 2"}
    
      # Inserting
      changeset =
        %Post{title: "1"}
        |> Ecto.Changeset.change
        |> Ecto.Changeset.put_assoc(:categories, [c1, c2])
      post = Repo.insert!(changeset)
      IO.inspect post
    end
    
    def run_update do
      c1 = Repo.insert! %Category{name: "cat update"}
      c2 = %Category{name: "cat 2"}
      post = Repo.get!(Post, 1) |> Repo.preload(:categories)
      # Updating
      changeset =
        post
        |> Ecto.Changeset.change
        |> Ecto.Changeset.put_assoc(:categories, [c1])
      post = Repo.update!(changeset)
      IO.inspect post
    end
    

    It is a partial solution, because if i want to update the related categories (Post already has a list of related categories) I have to remove and then save the empty list of categories first. Is it possible to do this in one go?

    def run_update_2 do
      c2 = Repo.get!(Tag, 2)
      # Assumes Post 1 already has  a few categories in it (for example after
      # running run_update()
      post = Repo.get!(Post, 1) |> Repo.preload(:categories)
    
      # Remove and add again
      changeset =
        post
        |> Ecto.Changeset.change
        |> Ecto.Changeset.put_assoc(:categories, [])
      IO.inspect changeset
      post = Repo.update!(changeset)
    
      changeset =
        post
        |> Ecto.Changeset.change
        |> Ecto.Changeset.put_assoc(:categories, [c2])
    
      post = Repo.update!(changeset)
      IO.inspect post
    end
    

提交回复
热议问题