What is the correct way to use Ecto.Query that allow items to be displayed in templates?

Context: I am trying to write a custom Ecto.Query to replace the standard one in my schema. The reason for this is that I am implemented a Person super type that will be shared by Users, External Authors, Subjects, etc. The query will join the info from the Person to the User.

The issue is that the standard user query works and correctly displays the information in the rendered template.

schema

  def get_user!(id) do
    Repo.get!(User, id)
  end

The equivalent Ecto.Query produces a Argument Error when attempting to access it in the template.

  def get_user_test(id) do
      Repo.all(from u in User, where: u.id == ^id)
  end

controller

  def show(conn, %{"id" => id}) do
    user = Core.get_user_test(id)
    render(conn, "show.html", user: user)
  end

template

li
  strong Email:
  = @user.email
  span = link "Edit", to: user_path(@conn, :edit, @user)

After many hours of research, I think it has to do with how the data is returned from Ecto.Query.

iex(6)> user = Franklin.Repo.all(from u in Franklin.Core.User, where: u.id == 4) 
[debug] QUERY OK source="core_users" db=3.5ms
SELECT c0."id", c0."email", c0."person_id", c0."inserted_at", c0."updated_at" FROM "core_users" AS c0 WHERE (c0."id" = 4) []
[%Franklin.Core.User{__meta__: #Ecto.Schema.Metadata<:loaded, "core_users">,
  email: "test@mail.com", id: 4, inserted_at: ~N[2017-07-28 17:58:25.072752],
  person: #Ecto.Association.NotLoaded<association :person is not loaded>,
  person_id: 2, updated_at: ~N[2017-07-28 17:58:25.072760]}]

iex(9)> user.email
** (ArgumentError) argument error
    :erlang.apply([%Franklin.Core.User{__meta__: #Ecto.Schema.Metadata<:loaded, "core_users">, email: "test@mail.com", id: 4, inserted_at: ~N[2017-07-28 17:58:25.072752], person: #Ecto.Association.NotLoaded<association :person is not loaded>, person_id: 2, updated_at: ~N[2017-07-28 17:58:25.072760]}], :email, [])

iex(11)> user[:email]
nil

https://elixirnation.io/references/ecto-query-examples

note- I know how to preload the assoc and add the joins, but have simplified the query to focus on troubleshooting the basic data first.

I am still new to Phoenix and Elixir. This is probably a newbie error and I would like to learn the right way instead of hacking my way to a solution.

Any help would be appreciated.

Core.get_user_test(id) seems to return a list of users. That’s because Repo.all returns a list.

Try either this (quick hack)

# controller
def show(conn, %{"id" => id}) do
  [user] = Core.get_user_test(id)
  render(conn, "show.html", user: user)
end

Or change the Core.get_user_test(id) to this

def get_user_test(id) do
  Repo.one(from u in User, where: u.id == ^id)
end

# controller
def show(conn, %{"id" => id}) do
  user = Core.get_user_test(id)
  render(conn, "show.html", user: user)
end
1 Like

Repo.all returns a list, if you only expect a single item use one of the different gets or one.

1 Like

Thanks for both suggestions. I ended up changing to use Repo.one in the query.