Ecto.Association.NotLoaded on preloading

Hello,
using a basic crud controller in phoenix,

tables = Tables.list_tables()
IO.inspect(tables, label: "Tables=====>")

this gives me

Tables=====>: [
  %App.Tables.Table{
    # some data
    id: 1,
    venue_id: 1,
  },
  %App.Tables.Table{
    # some data
    id: 2,
    venue_id: 2,
  %App.Tables.Table{
    # some data
    id: 4,
    venue_id: 2,
  }
]
  

From this module, I can sort the Tables by given venue_id,

 def get_tables_by_venue(venue_id) do
    filter = from(t in Table, where: t.venue_id == ^venue_id, order_by: [desc: t.inserted_at])
    Repo.all(filter)
  end

Tables has many_to_many with groups.

IO.inspect(group.tables, label: "GroupTables=====>:)

GroupTables=====>: [
  %App.Tables.Table{
    # some data
    id: 1,
    venue_id: 1,
  %App.Tables.Table{
    # some data
    id: 4,
    venue_id: 2,
  }
]

However, if I try this:

def get_group_tables_by_venue(%Group{} = group, venue_id) do
  tables = group.tables
  filter = from(t in tables, where: t.venue_id == ^venue_id, order_by: [desc: t.inserted_at])
  Repo.all(filter)
end

i get protocol Ecto.Queryable not implemented for #Ecto.Association.NotLoaded<association :tables is not loaded> of type Ecto.Association.NotLoaded (a struct). This protocol is implemented for the following type(s): Atom, BitString, Ecto.Query, Ecto.SubQuery, Tuple.
Sure, if I preload tables

def get_group_tables_by_venue(%Group{} = group, venue_id) do
  tables = group.tables |> Repo.preload(:tables)
  filter = from(t in tables, where: t.venue_id == ^venue_id, order_by: [desc: t.inserted_at])
  Repo.all(filter)
end

I get function Ecto.Association.NotLoaded.__schema__/2 is undefined or private. And I have imported

import Ecto.Query, warn: false

Why?

Best Regards

It seems that you are trying to query group.tables, and this relationship is not loaded. I might be wrong, but I don’t think you can pass a relationship to from. Instead try filtering with the parent ids like: from(t in GroupTables, where: t.venue_id == ^venue_id and group_id: group.id and table_id: table.id, order_by: [desc: t.inserted_at])

PS.: I think you could also Repo.preload(group, [:tables]) and filter from that if you don’t have the parent ids available. Repo.preload/3 also accepts a query.

Instead of group.tables, I think you want Ecto.Query.assoc(group, :tables) (docs) which builds a Queryable:

def get_group_tables_by_venue(%Group{} = group, venue_id) do
  tables = assoc(group, :tables)
  filter = from(t in tables, where: t.venue_id == ^venue_id, order_by: [desc: t.inserted_at])
  Repo.all(filter)
end
2 Likes

Actually yes, it did the job. It was a tricky case, Thanks for your time and answer, and have a good day :slight_smile: