I’m trying to learn how to create a many to many relation as explained in What's new in Ecto 2.0
. I’m able to create new relations in my app between people and books with this code, but when I try to update a relation I’m getting this error:
you are attempting to change relation :people of MyDb.Book, but there is missing data.
/web/models/book.ex
defmodule MyDb.Book do
use MyDb.Web, :model
schema "books" do
field :title, :string
field :note, :string
many_to_many :people, MyDb.Person, join_through: MyDb.BookPerson
timestamps()
end
@doc """
Builds a changeset based on the `struct` and `params`.
"""
def changeset(struct, params \\ %{}) do
struct
|> cast(params, [:title, :note])
|> put_assoc(:people, parse_people_ids(params))
end
defp parse_people_ids(params) do
(params["people_ids"] || [])
|> Enum.map(&get_people/1)
end
defp get_people(id) do
MyDb.Repo.get_by(MyDb.Person, id: id)
end
end
I am able to save relation with this changeset.
/web/controllers/book_controller.ex
def edit(conn, %{"id" => id}) do
book = Repo.get!(Book, id)|> Repo.preload(:people)
people = Repo.all(Person) |> Enum.map(&{&1.surname, &1.id})
changeset = Book.changeset(book)
render(conn, "edit.html", book: book, changeset: changeset, conn: conn, people: people)
end
def update(conn, %{"id" => id, "book" => book_params}) do
book = Repo.get!(Book, id)
changeset = Book.changeset(book, book_params)
case Repo.update(changeset) do
{:ok, book} ->
conn
|> put_flash(:info, "Book updated successfully.")
|> redirect(to: book_path(conn, :show, book))
{:error, changeset} ->
render(conn, "edit.html", book: book, changeset: changeset)
end
end
/web/templates/book/form.html.eex
<div class="form-group">
<%= label f, "Authors", class: "control-label" %>
<%= multiple_select f, :people_ids, @people, class: "form-control" %>
<%= error_tag f, :person_id %>
</div>
I’m only learning Phoenix and Elixir so I’m probably missing something pretty essential. Could someone please explain what is wrong with this code?