Search code examples
elixirecto

Not being able to preload has_many through associated data


My association are:

defmodule Mp.Schema.User do
  use Ecto.Schema
  import Ecto.Changeset

  schema "users" do
    # ...
    has_many :user_gyms, UserGym, on_replace: :delete
    has_many :gyms, through: [:user_gyms, :gyms]
  end
end

defmodule Mp.Schema.UserGym do
  use Ecto.Schema
  import Ecto.Changeset

  schema "user_gyms" do
    # ...
    belongs_to(:user, User)
    belongs_to(:gym, Gym)
  end
end

defmodule Mp.Schema.Gym do
  use Ecto.Schema
  import Ecto.Changeset

  schema "gyms" do
    # ...
    has_many :user_gyms, UserGym, on_replace: :delete
    has_many :users, through: [:user_gyms, :users]
  end
end

I am trying to preload gyms:

query = from U in Mp.Schema.User, preload: :gyms
Mp.Repo.all(query)

But i am getting this error:

schema Mp.Schema.UserGym does not have association or embed :gyms

There seems to have issue on association but I think I have fulfilled all requirements. any suggestion?


Solution

  • The reason this is failing for you is that you're using :gyms and :users in the :through lists. Those keys should refer to the associations on UserGym, which are :user and :gym, so, your code should look like:

    defmodule Mp.Schema.User do
      use Ecto.Schema
      import Ecto.Changeset
    
      schema "users" do
        # ...
        has_many :user_gyms, UserGym, on_replace: :delete
        has_many :gyms, through: [:user_gyms, :gym]
      end
    end
    ...
    defmodule Mp.Schema.Gym do
      use Ecto.Schema
      import Ecto.Changeset
    
      schema "gyms" do
        # ...
        has_many :user_gyms, UserGym, on_replace: :delete
        has_many :users, through: [:user_gyms, :user]
      end
    end
    

    So, for instance :gyms is created as an association through the existing :user_gyms of the current schema and :gym on that schema.

    If you look at the documentation for has_many/3, you'll see there's an example using through and it has a comment that says:

    # In the has_many :through example below, the `:comments`
    # in the list [:comments, :author] refers to the
    # `has_many :comments` in the Post own schema and the
    # `:author` refers to the `belongs_to :author` of the
    # Comment's schema (the module below).