Search code examples
elixirphoenix-frameworkecto

Phoenix model with has_many relation will not update w/o preloading the relation


I'm working on a Phoenix application that's a personal version of Evernote. I have a Book model, that has_many Note records.

This is my book model:

defmodule Notebook.Book do
  use Notebook.Web, :model

  schema "books" do
    field :name, :string, default: ""
    has_many :notes, Notebook.Note
    belongs_to :user, Notebook.User
    timestamps()
  end

  @doc """
    Book changeset. Name field required.
  """
  def changeset(model, params \\ %{}) do
    model
    |> cast(params, [:name])
    |> validate_required(:name)
  end
end

And I have an update endpoint in my controller:

def update(conn, %{"id" => id, "book" => book_params}) do
  existing_book = Repo.get(Book, id)
  changeset = Book.changeset(existing_book, book_params)

  case Repo.insert(changeset) do
    {:ok, book} ->
      conn
      |> put_status(:ok)
      |> render("show.json", book: book)

    {:error, changeset} ->
      conn
      |> put_status(:unprocessable_entity)
      |> render("error.json", message: changeset.errors)
  end
end

And a test:

test "with a valid jwt", %{conn: conn, jwt: jwt} do
  book = insert(:book)
  resp = conn
  |> put_req_header("authorization", "Bearer: #{jwt}")
  |> put(book_path(Endpoint, :update, book, book: %{name: "New  Book"}))
  |> json_response(:ok)

  assert resp["data"]["book"]["name"] == "New Book"
end

When I run my tests, I get this error:

** (RuntimeError) attempting to cast or change association `notes`   from `Notebook.Book` that was not loaded. Please preload your associations before manipulating them through changesets

The params I'm sending are only name. Looking into this, I found a related issue, but since I'm not using cast_assoc I don't think it applies.

I can't figure out what I'm doing wrong here. I understand (I think) about preloading relations in Ecto, but in this case I'm not updating the dependent Note records, just the one field of the Book record, so I should not need to preload.

The entire repo is here.


Solution

  • Looks like you're using Repo.insert in the update method of BookController. Changing that to Repo.update should fix it.