Problem with preloading ecto associations

Hello Devs,

I’m doing an ecto association with many_to_many - websites and tags.
The Logic: Website has tags, many tags belongs to one or more website.

Anyway, yesterday I tried a relationship between tags and websites (website = tag.websites) and I got an argument error (code bellow). However, it turned out that the solution is to preload :websites inside tags’ “categories.ex” context. But now, doing the same relationship between websites and tags is not working. Error:argument error I assume that again the preload is involved, but how?

#tag_controller.ex
  def show(conn, %{"id" => id}) do
    tag = Categories.get_tag!(id)
    website = tag.websites
    tags_for_website = website.tags
    render(conn, "show.html", tag: tag, websites_for_tag: website, tags_for_website: tags_for_website)
  end
#categories.ex (Tags' context)
  def get_tag!(id) do
    Repo.get!(Tag, id)
    |> Repo.preload(:websites)
  end
#home.ex (Websites' context)
  def get_websites!(id) do
    Repo.get!(Websites, id)
    |> Repo.preload(:tags)
  end

and html which for me is right:

#show.html.eex

(...)
         <%= for tag <- @tags_for_website do %>

         <% end %>
(...)

Any thoughts would be appreciated,

Best Regards,
ykostov

1 Like

“argument error” is the BEAM’s version of “something went wrong” - by itself, it doesn’t tell you anything specific about what went happened. Can you post the complete error message?

One common cause of unexpected errors when preloading is misconfigured associations in the schemas, so posting the code for the Websites and Tag schemas would be very helpful.

Sure, the whole error from the terminal is:

(exit) an exception was raised:
    ** (ArgumentError) argument error
        :erlang.apply([%BgsiteOfficial.Home.Websites{__meta__: #Ecto.Schema.Metadata<:loaded, "websites">, banner: %{file_name: "grisho3.jpeg", updated_at: ~N[2021-04-04 08:59:16]}, description: "Новини Тенис Политика", id: 2, inserted_at: ~N[2021-04-04 08:59:16], likes: 5, tags: #Ecto.Association.NotLoaded<association :tags is not loaded>, title: "Sites.bg", updated_at: ~N[2021-04-04 08:59:16], urls: "sites.bg"}, %BgsiteOfficial.Home.Websites{__meta__: #Ecto.Schema.Metadata<:loaded, "websites">, banner: %{file_name: "grigor-nadal.jpeg", updated_at: ~N[2021-03-31 13:13:25]}, description: "Спорт , Тенис", id: 1, inserted_at: ~N[2021-03-31 13:13:25], likes: 5, tags: #Ecto.Association.NotLoaded<association :tags is not loaded>, title: "Offside24.net", updated_at: ~N[2021-04-05 17:45:48], urls: "offside24.net"}], :tags, [])
        (bgsite_official 0.1.0) lib/bgsite_official_web/controllers/tag_controller.ex:32: BgsiteOfficialWeb.TagController.show/2
        (bgsite_official 0.1.0) lib/bgsite_official_web/controllers/tag_controller.ex:1: BgsiteOfficialWeb.TagController.action/2
        (bgsite_official 0.1.0) lib/bgsite_official_web/controllers/tag_controller.ex:1: BgsiteOfficialWeb.TagController.phoenix_controller_pipeline/2
        (phoenix 1.5.7) lib/phoenix/router.ex:352: Phoenix.Router.__call__/2
        (bgsite_official 0.1.0) lib/bgsite_official_web/endpoint.ex:1: BgsiteOfficialWeb.Endpoint.plug_builder_call/2
        (bgsite_official 0.1.0) lib/plug/debugger.ex:132: BgsiteOfficialWeb.Endpoint."call (overridable 3)"/2
        (bgsite_official 0.1.0) lib/bgsite_official_web/endpoint.ex:1: BgsiteOfficialWeb.Endpoint.call/2
        (phoenix 1.5.7) lib/phoenix/endpoint/cowboy2_handler.ex:65: Phoenix.Endpoint.Cowboy2Handler.init/4
        (cowboy 2.8.0) /home/almeida/bgsite_official/deps/cowboy/src/cowboy_handler.erl:37: :cowboy_handler.execute/2
        (cowboy 2.8.0) /home/almeida/bgsite_official/deps/cowboy/src/cowboy_stream_h.erl:300: :cowboy_stream_h.execute/3
        (cowboy 2.8.0) /home/almeida/bgsite_official/deps/cowboy/src/cowboy_stream_h.erl:291: :cowboy_stream_h.request_process/3
        (stdlib 3.14) proc_lib.erl:226: :proc_lib.init_p_do_apply/3

#tag.ex
  schema "tags" do
    field :banner, :string
    field :description, :string
    field :likes, :integer
    field :title, :string

    timestamps()

    many_to_many(
      :websites,
      Websites,
      join_through: "website_tag",
      on_replace: :delete
    )
  end
#websites.ex
schema "websites" do
    field :banner, BgsiteOfficial.BannerUploader.Type
    field :description, :string
    field :likes, :integer
    field :title, :string
    field :urls, :string
    timestamps()

    many_to_many(
      :tags,
      Tag,
      join_through: "website_tag",
      on_replace: :delete
    )
  end

Resolved by using website = websites = tag.websites |> Repo.preload(:tags)

Be careful about naming - naming the variable website here obscures the reality that tag.websites returns a list of Websites structs.

That leads to errors like the argument error you’re seeing, where website.tags is expecting a struct but gets a list and fails.

2 Likes