lapinkoira

lapinkoira

Issue with Ecto query preloading

Hi all, I am reading the Programming Phoenix book and following the steps to implement the Rumbl webapp.

Then at some steps I try to add something custom to learn a bit more.

So in that application, I have a controller which renders a template like this:

Listing videos

<%= for video <- @videos do %>
  <td class="text-right">
    <%= link "Show", to: video_path(@conn, :show, video), class: "btn btn-default btn-xs" %>
    <%= link "Edit", to: video_path(@conn, :edit, video), class: "btn btn-default btn-xs" %>
    <%= link "Delete", to: video_path(@conn, :delete, video), method: :delete, data: [confirm: "Are you sure?"], class: "btn btn-danger btn-xs" %>
  </td>
</tr>

<% end %>

User Url Title Description Category
<%= video.user_id %> <%= video.url %> <%= video.title %> <%= video.description %> <%= if category = video.category, do: category.name %>

<%= link “New video”, to: video_path(@conn, :new) %>

I added this line to render the categories <%= if category = video.category, do: category.name %>

This is my controller
def index(conn, _params, user) do
videos = Repo.all(user_videos(user)) |> Repo.preload(:category)
render(conn, “index.html”, videos: videos)
end

defp user_videos(user) do
# query user videos
assoc(user, :videos)
end

And when I run a test I wrote, this one:

test “renders index.html”, %{conn: conn} do
videos = [%Rumbl.Video{id: “1”, title: “dogs”},
%Rumbl.Video{id: “2”, title: “cats”}]
content = render_to_string(Rumbl.VideoView, “index.html”,
conn: conn, videos: videos)

assert String.contains?(content, "Listing videos")
for video <- videos do
  assert String.contains?(content, video.title)
end

end

It complains with categories not being preloaded

** (KeyError) key :name not found in: #Ecto.Association.NotLoaded

I also tried this:

defp user_videos(user) do
query = (from u in user, select: u.videos, preload: [:category])
Repo.all(query)
end

But not working neither, so how can I preload them?

I thought I just needed to preload them at the pipe with |> Repo.preload(:category)

Also the strange thing is this started to crash with test, without the test it renders the index.html template without any issue

Marked As Solved

dom

dom

It doesn’t matter what you do in the controller, since this test tests the view, not the controller.

videos = [%Rumbl.Video{id: "1", title: "dogs"}, %Rumbl.Video{id: "2", title: "cats"}]

You need to preload the category for these videos (in the test itself). Here that could mean just hardcoding it like:

videos = [%Rumbl.Video{id: "1", title: "dogs", category: %Rumbl.Category{name: “Animals”}}, ...]

Also Liked

axelson

axelson

Scenic Core Team

I have a little helper function that might be useful to you:

  def clear_associations(%{__struct__: struct} = schema) do
    struct.__schema__(:associations)
    |> Enum.reduce(schema, fn association, schema ->
      %{schema | association => build_not_loaded(struct, association)}
    end)
  end

  defp build_not_loaded(struct, association) do
    %{
      cardinality: cardinality,
      field: field,
      owner: owner
    } = struct.__schema__(:association, association)

    %Ecto.Association.NotLoaded{
      __cardinality__: cardinality,
      __field__: field,
      __owner__: owner
    }
  end

With that you could write a assert_matches_without_associations function. Just call clear_associations/1 on each input and then assert equality.

Although (in line with what @LostKobrakai is saying) I actually use this assert_ids_match/2 helper more:

  @doc """
  Helper for checking that for two structs, or two lists of structs have the
  same id keys
  """
  def assert_ids_match(list1, list2) when is_list(list1) and is_list(list2) do
    list1_ids =
      list1
      |> Enum.map(& &1.id)
      |> Enum.sort()

    list2_ids =
      list2
      |> Enum.map(& &1.id)
      |> Enum.sort()

    assert list1_ids == list2_ids
  end

  def assert_ids_match(%{id: id1}, %{id: id2}) do
    assert id1 == id2
  end
LostKobrakai

LostKobrakai

Maybe this can shed some light:

jc00ke

jc00ke

Awesome, thanks for the code! I’ll probably head the assert_ids_match route soon; it’s good enough for me.

Last Post!

jc00ke

jc00ke

Awesome, thanks for the code! I’ll probably head the assert_ids_match route soon; it’s good enough for me.

Where Next?

Popular in Questions Top

RisingFromAshes
I’ve read in another post that it may be possible with a router helper - but I couldn’t find an appropriate one, and tbh, I’m still just ...
New
minhajuddin
I have seen a lot of code which picks the first element from a list using Enum.at(0) instead of List.first. Is there a reason why people ...
New
joaquinalcerro
Hi there, I am working with Ecto-Postgresql and I need to call all of the records from a specific table but the table has 40,000 records...
New
hariharasudhan94
I would like to know what is the best IDE for elixir development?
New
joeerl
Hello again - after a longish gap I’ve decided I really must dig into Elixir and see what’s been happening here - so I have a few questio...
New
marius95
Hello everyone, I try to use an Javascript Event Handler in my root.html.leex file. Therefore I created a function in the app.js file: ...
New
dblack
I’ve got an issue with an app and I’ve no idea of how to troubleshoot it. I’m hoping someone here might have seen something similar. I p...
New

Other popular topics Top

JeremM34
Hello, how can I check the Phoenix version ? Thanks !
New
aadeshere1
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible. total = 10 while total != 0 ...
New
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New
sergio
Kind of like when jquery came out, it was super necessary. Existing drag and drop libraries have a bunch of baggage to support old browse...
New
AstonJ
Posting this to see if we can make things easier for people to get into Neovim. If you use Neovim and have a favourite distro please let ...
New