What I am doing wrong: Protocol.UndefinedError) protocol Enumerable not implemented

I am following this tutorial. I have followed carefully correcting errors as I go. The app allows you to create a blog post and add comments if you so choose. I got the Blog post part up and running, I am having difficulty setting up the add comments part when I click show I get this error:

error] #PID<0.918.0> running BlogAppWeb.Endpoint (connection #PID<0.797.0>, stream id 13) terminated
Server: localhost:4000 (http)
Request: GET /posts/1
** (exit) an exception was raised:
    ** (Protocol.UndefinedError) protocol Enumerable not implemented for #Ecto.Association.NotLoaded<association :comments is not loaded> of type Ecto.Association.NotLoaded (a struct)
        (elixir 1.13.3) lib/enum.ex:1: Enumerable.impl_for!/1
        (elixir 1.13.3) lib/enum.ex:143: Enumerable.reduce/3
        (elixir 1.13.3) lib/enum.ex:4144: Enum.reduce/3
        (blog_app 0.1.0) lib/blog_app_web/templates/post/comments.html.eex:7: BlogAppWeb.PostView."comments.html"/1
        (blog_app 0.1.0) lib/blog_app_web/templates/post/show.html.heex:19: anonymous fn/2 in BlogAppWeb.PostView."show.html"/1
        (phoenix_live_view 0.17.7) lib/phoenix_live_view/engine.ex:124: Phoenix.HTML.Safe.Phoenix.LiveView.Rendered.to_iodata/1
        (phoenix_live_view 0.17.7) lib/phoenix_live_view/engine.ex:140: Phoenix.HTML.Safe.Phoenix.LiveView.Rendered.to_iodata/3
        (phoenix 1.6.6) lib/phoenix/controller.ex:772: Phoenix.Controller.render_and_send/4
        (blog_app 0.1.0) lib/blog_app_web/controllers/post_controller.ex:1: BlogAppWeb.PostController.action/2
        (blog_app 0.1.0) lib/blog_app_web/controllers/post_controller.ex:1: BlogAppWeb.PostController.phoenix_controller_pipeline/2
        (phoenix 1.6.6) lib/phoenix/router.ex:355: Phoenix.Router.__call__/2
        (blog_app 0.1.0) lib/blog_app_web/endpoint.ex:1: BlogAppWeb.Endpoint.plug_builder_call/2
        (blog_app 0.1.0) lib/plug/debugger.ex:136: BlogAppWeb.Endpoint."call (overridable 3)"/2
        (blog_app 0.1.0) lib/blog_app_web/endpoint.ex:1: BlogAppWeb.Endpoint.call/2
        (phoenix 1.6.6) lib/phoenix/endpoint/cowboy2_handler.ex:54: Phoenix.Endpoint.Cowboy2Handler.init/4
        (cowboy 2.9.0) /Users/shansiddiqui/Desktop/blog_app/deps/cowboy/src/cowboy_handler.erl:37: :cowboy_handler.execute/2
        (cowboy 2.9.0) /Users/shansiddiqui/Desktop/blog_app/deps/cowboy/src/cowboy_stream_h.erl:306: :cowboy_stream_h.execute/3
        (cowboy 2.9.0) /Users/shansiddiqui/Desktop/blog_app/deps/cowboy/src/cowboy_stream_h.erl:295: :cowboy_stream_h.request_process/3
        (stdlib 3.17) proc_lib.erl:226: :proc_lib.init_p_do_apply/3

and this is what comes on my webpage

My current Github has the updated app, can someone help me figure out what I am doing wrong?

You need to preload the comments association of the blog before you try to render it. Ecto does not try to read your mind or have mutable state for lazy loading as other db frameworks do.

2 Likes

I am sorry I am a complete beginner here, Would kindly guide me. How could I reload the comments first? the whole page loads at the same time.

1 Like

<%= for comment <- @comments do %> this part tries to iterate over comments, but the comments don’t exist because as top comment mentions you haven’t loaded them.

in order to access a related model you need to preload it

1 Like

In reference to the tutorial, the guide does not do this? Is this because the tutorial is outdated

I don’t typically go read a beginner tutorial someone else is following, however, in this case I searched the page for “preload” and found under section 3 where comments are added, this is added to the posts_controller.ex

def add_comment(conn, %{"comment" => comment_params, "post_id" => post_id}) do
	post =
		post_id
		|> Posts.get_post!()
		|> Repo.preload([:comments])

That last line is preloading comments. And a couple of paragraphs later the blog states

We need to preload comments from the Repo …

1 Like

Here is my update :
This is my changeset Func:

 def changeset(comment, attrs) do
    comment
    |> cast(attrs, [:name, :content, :post_id])
    |> validate_required([:name, :content, :post_id])
  end
end

When I try to create a new Post I get an error that says changeset Function NOT found clearly its right there!!

Error

[info] Sent 302 in 83ms
[info] GET /posts/3
[debug] Processing with BlogAppWeb.PostController.show/2
  Parameters: %{"id" => "3"}
  Pipelines: [:browser]
[debug] QUERY OK source="posts" db=4.4ms queue=0.3ms idle=1176.0ms
SELECT p0."id", p0."body", p0."title", p0."inserted_at", p0."updated_at" FROM "posts" AS p0 WHERE (p0."id" = $1) [3]
[info] Sent 500 in 95ms
[error] #PID<0.622.0> running BlogAppWeb.Endpoint (connection #PID<0.572.0>, stream id 14) terminated
Server: localhost:4000 (http)
Request: GET /posts/3
** (exit) an exception was raised:
    ** (KeyError) key :changeset not found in: %{conn: %Plug.Conn{adapter: {Plug.Cowboy.Conn, :...}, assigns: %{layout: {BlogAppWeb.LayoutView, "app.html"}, post: %BlogApp.Posts.Post{__meta__: #Ecto.Schema.Metadata<:loaded, "posts">, body: "Test # 3", comments: #Ecto.Association.NotLoaded<association :comments is not loaded>, id: 3, inserted_at: ~N[2022-03-18 17:50:15], title: "Hello", updated_at: ~N[2022-03-18 17:50:15]}}, body_params: %{}, cookies: %{"_blog_app_key" => "SFMyNTY.g3QAAAACbQAAAAtfY3NyZl90b2tlbm0AAAAYSjBybkJSX2U0ZHVyalNoLVkzeHhCdlRtbQAAAA1waG9lbml4X2ZsYXNodAAAAAFtAAAABGluZm9tAAAAGlBvc3QgY3JlYXRlZCBzdWNjZXNzZnVsbHku.-BzmBjG1xDvxVI_skwFYETUR8AELIPVemAJIDpKWZFs", "_live_view_todos_key" => "SFMyNTY.g3QAAAABbQAAAAtfY3NyZl90b2tlbm0AAAAYWWJ1XzE5MnJSSkV6OVl3eVl0cUpvM2pN.NJYDbH-n7JlaQFMHL2_x-T2zhhvyYD6XBmo8GequxP8"}, halted: false, host: "localhost", method: "GET", owner: #PID<0.622.0>, params: %{"id" => "3"}, path_info: ["posts", "3"], path_params: %{"id" => "3"}, port: 4000, private: %{BlogAppWeb.Router => {[], %{Plug.Swoosh.MailboxPreview => ["mailbox"]}}, :before_send => [#Function<0.16477574/1 in Plug.CSRFProtection.call/2>, #Function<2.17183421/1 in Phoenix.Controller.fetch_flash/2>, #Function<0.77458138/1 in Plug.Session.before_send/2>, #Function<0.23023616/1 in Plug.Telemetry.call/2>, #Function<1.46227650/1 in Phoenix.LiveReloader.before_send_inject_reloader/3>], :phoenix_action => :show, :phoenix_controller => BlogAppWeb.PostController, :phoenix_endpoint => BlogAppWeb.Endpoint, :phoenix_flash => %{"info" => "Post created successfully."}, :phoenix_format => "html", :phoenix_layout => {BlogAppWeb.LayoutView, :app}, :phoenix_request_logger => {"request_logger", "request_logger"}, :phoenix_root_layout => {BlogAppWeb.LayoutView, :root}, :phoenix_router => BlogAppWeb.Router, :phoenix_template => "show.html", :phoenix_view => BlogAppWeb.PostView, :plug_session => %{"_csrf_token" => "J0rnBR_e4durjSh-Y3xxBvTm", "phoenix_flash" => %{"info" => "Post created successfully."}}, :plug_session_fetch => :done}, query_params: %{}, query_string: "", remote_ip: {127, 0, 0, 1}, req_cookies: %{"_blog_app_key" => "SFMyNTY.g3QAAAACbQAAAAtfY3NyZl90b2tlbm0AAAAYSjBybkJSX2U0ZHVyalNoLVkzeHhCdlRtbQAAAA1waG9lbml4X2ZsYXNodAAAAAFtAAAABGluZm9tAAAAGlBvc3QgY3JlYXRlZCBzdWNjZXNzZnVsbHku.-BzmBjG1xDvxVI_skwFYETUR8AELIPVemAJIDpKWZFs", "_live_view_todos_key" => "SFMyNTY.g3QAAAABbQAAAAtfY3NyZl90b2tlbm0AAAAYWWJ1XzE5MnJSSkV6OVl3eVl0cUpvM2pN.NJYDbH-n7JlaQFMHL2_x-T2zhhvyYD6XBmo8GequxP8"}, req_headers: [{"accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"}, {"accept-encoding", "gzip, deflate, br"}, {"accept-language", "en-US,en;q=0.9"}, {"cache-control", "max-age=0"}, {"connection", "keep-alive"}, {"cookie", "_live_view_todos_key=SFMyNTY.g3QAAAABbQAAAAtfY3NyZl90b2tlbm0AAAAYWWJ1XzE5MnJSSkV6OVl3eVl0cUpvM2pN.NJYDbH-n7JlaQFMHL2_x-T2zhhvyYD6XBmo8GequxP8; _blog_app_key=SFMyNTY.g3QAAAACbQAAAAtfY3NyZl90b2tlbm0AAAAYSjBybkJSX2U0ZHVyalNoLVkzeHhCdlRtbQAAAA1waG9lbml4X2ZsYXNodAAAAAFtAAAABGluZm9tAAAAGlBvc3QgY3JlYXRlZCBzdWNjZXNzZnVsbHku.-BzmBjG1xDvxVI_skwFYETUR8AELIPVemAJIDpKWZFs"}, {"host", "localhost:4000"}, {"referer", "http://localhost:4000/posts/new"}, {"sec-ch-ua", "\" Not A;Brand\";v=\"99\", \"Chromium\";v=\"98\", \"Google Chrome\";v=\"98\""}, {"sec-ch-ua-mobile", "?0"}, {"sec-ch-ua-platform", "\"macOS\""}, {"sec-fetch-dest", "document"}, {"sec-fetch-mode", "navigate"}, {"sec-fetch-site", "same-origin"}, {"sec-fetch-user", "?1"}, {"upgrade-insecure-requests", "1"}, {"user-agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36"}], request_path: "/posts/3", resp_body: nil, resp_cookies: %{}, resp_headers: [{"cache-control", "max-age=0, private, must-revalidate"}, {"x-request-id", "Ft2K8xb5O7AJv6MAAANC"}, {"x-frame-options", "SAMEORIGIN"}, {"x-xss-protection", "1; mode=block"}, {"x-content-type-options", "nosniff"}, {"x-download-options", "noopen"}, {"x-permitted-cross-domain-policies", "none"}, {"cross-origin-window-policy", "deny"}], scheme: :http, script_name: [], secret_key_base: :..., state: :unset, status: nil}, post: %BlogApp.Posts.Post{__meta__: #Ecto.Schema.Metadata<:loaded, "posts">, body: "Test # 3", comments: #Ecto.Association.NotLoaded<association :comments is not loaded>, id: 3, inserted_at: ~N[2022-03-18 17:50:15], title: "Hello", updated_at: ~N[2022-03-18 17:50:15]}}
        (blog_app 0.1.0) lib/blog_app_web/templates/post/show.html.heex:19: anonymous fn/2 in BlogAppWeb.PostView."show.html"/1
        (phoenix_live_view 0.17.7) lib/phoenix_live_view/engine.ex:124: Phoenix.HTML.Safe.Phoenix.LiveView.Rendered.to_iodata/1
        (phoenix_live_view 0.17.7) lib/phoenix_live_view/engine.ex:140: Phoenix.HTML.Safe.Phoenix.LiveView.Rendered.to_iodata/3
        (phoenix 1.6.6) lib/phoenix/controller.ex:772: Phoenix.Controller.render_and_send/4
        (blog_app 0.1.0) lib/blog_app_web/controllers/post_controller.ex:1: BlogAppWeb.PostController.action/2
        (blog_app 0.1.0) lib/blog_app_web/controllers/post_controller.ex:1: BlogAppWeb.PostController.phoenix_controller_pipeline/2
        (phoenix 1.6.6) lib/phoenix/router.ex:355: Phoenix.Router.__call__/2
        (blog_app 0.1.0) lib/blog_app_web/endpoint.ex:1: BlogAppWeb.Endpoint.plug_builder_call/2
        (blog_app 0.1.0) lib/plug/debugger.ex:136: BlogAppWeb.Endpoint."call (overridable 3)"/2
        (blog_app 0.1.0) lib/blog_app_web/endpoint.ex:1: BlogAppWeb.Endpoint.call/2
        (phoenix 1.6.6) lib/phoenix/endpoint/cowboy2_handler.ex:54: Phoenix.Endpoint.Cowboy2Handler.init/4
        (cowboy 2.9.0) /Users/shansiddiqui/Desktop/blog_app/deps/cowboy/src/cowboy_handler.erl:37: :cowboy_handler.execute/2
        (cowboy 2.9.0) /Users/shansiddiqui/Desktop/blog_app/deps/cowboy/src/cowboy_stream_h.erl:306: :cowboy_stream_h.execute/3
        (cowboy 2.9.0) /Users/shansiddiqui/Desktop/blog_app/deps/cowboy/src/cowboy_stream_h.erl:295: :cowboy_stream_h.request_process/3
        (stdlib 3.17) proc_lib.erl:226: :proc_lib.init_p_do_apply/3
1 Like

General tip: you’ll get better help if you show the corresponding code along with the error message. What is on line 19 of lib/blog_app_web/templates/post/show.html.heex?

Based on the code up on Github, I’m guessing it’s the same code that’s on line 24 here:

The error is telling you that the view is looking for an assign named changeset but not finding it.

In the corresponding controller, changeset is passed to many other calls to render but not for the show action:

3 Likes