Not preloading ecto associations

How can I tell ecto that I don’t need an association. I have a user schema that has 2 associations

schema "users" do
    field :email, :string
    field :first_name, :string
    field :last_name, :string
    field :password, :string, virtual: true
    field :hashed_password, :string
    field :confirmed_at, :naive_datetime

    belongs_to :space, Space
    many_to_many :apps, App, join_through: "app_user", on_replace: :delete

    timestamps()
  end

When I try to list users

def list_space_users(space_id) do
    query =
      from u in User,
        where: u.space_id == ^space_id,
        select: u

    Repo.all(query)
  end

I get an association not loaded error.

#Ecto.Association.NotLoaded<association :space is not loaded>

I know I can preload the association but in my case it’s not needed I just want the user schema without its associations.

This is not an error, it’s a special struct Ecto puts in the field as a placeholder. If you don’t need it, as long as you don’t access the field it shouldn’t create any problems.

2 Likes

How do you call this function?

That makes perfect sense thank you! I was misreading the true meaning of my full error message.

I’m calling it in a controller

def index(conn, _params) do
    space_id = conn.assigns.current_user.space_id

    case Oriio.Accounts.list_space_users(space_id) do
      {:ok, users}  ->
        conn
        |> put_status(:ok)
        |> put_view(OriioWeb.UserView)
        |> render("users.json", users: users)
      {:error, changeset} ->
        conn
        |> put_status(:bad_request)
        |> put_view(OriioWeb.ChangesetView)
        |> render(:error, changeset: changeset)
    end
  end

The full error message though is.

no case clause matching: [#Oriio.Accounts.User<__meta__: #Ecto.Schema.Metadata<:loaded, "users">, apps: #Ecto.Association.NotLoaded<association :apps is not loaded>, confirmed_at: nil, email: "myemail@gmail.com", first_name: "updated", hashed_password: "$2b$12$hzqAJIzNTsKwvbfNkh8L7eNyjG/CLJRCGU7gpMcrBOyVyiufqGYx6", id: 3, inserted_at: ~N[2020-09-17 21:55:39], last_name: "updated", space: #Ecto.Association.NotLoaded<association :space is not loaded>, space_id: 25, updated_at: ~N[2020-10-08 20:30:56], ...>]

The true issue is {:ok, users} clause in the controller it should just be users.

I just skimmed through the error message and saw #Ecto.Association.NotLoaded<association :space is not . Automatically interpretted that as the main issue(I’m new to elixir). Instead I should just have read the first sentence of the error (no case clause matching) and everything would have made sense lol.

There’s no need for a case statement at all in your code because Repo.all only returns a list.

def index(conn, _params) do
  space_id = conn.assigns.current_user.space_id

  users = Oriio.Accounts.list_space_users(space_id)

  conn
  |> put_status(:ok)
  |> put_view(OriioWeb.UserView)
  |> render("users.json", users: users)
end

Thank you!! I have to say I’ve been learning Elixir now for about 3 weeks and the community has been amazing. Every time I post a question here I always get help and the responses are informative. As a total side note it would be nice if an IDE or even better the compiler could catch things like that . Do you have a list of editors/plugins you would recommend?

1 Like

Yes I have found the community around Elixir to be excellent. I would really recommend the slack channel if you’re just getting started for even quicker help :slight_smile:

Well, there is quite a bit of interest in a statically typed Elixir variant, which I think is what you would need for the compiler to catch that kind of thing, though I’m not sure. You can find various threads aroudn the forum on that issue. Otherwise, typespecs with dialyzer can help a lot.

I waited a while to get into typespecs after learning Elixir and I wish I had started earlier. They really are incredibly helpful for catching silly problems early, getting ahead on documentation, and refactoring more often.

One option: Dialyzer can help find errors like this one, where you have a function that returns a list() but a case that’s trying to match {:ok, list()}. I hear the VSCode / ElixirLS combo is a good IDE-ish experience.

Thank you! I’m gonna give it a try. I had come across Dialyzer but most ppl have iffy feeling about it so I wasn’t sure how idiomatic it was to use. WhatsApp announced they have a typed version of erlang coming out this November so that’s something exciting to keep an eye on. Slides from the talk