josefrichter

josefrichter

Where do you preload associations?

Simple question re. code organization: do you do preloads in context, or in controller/liveview?

Say a basic function in context looks like this

def get_class!(id) do
    Repo.get!(Class, id)
    |> Repo.preload(:users)
end

But basically sometimes I need the association, sometimes I don’t.

So would you typically do get_class!(id) |> Repo.preload(:users) in controller/liveview? (I have a feeling that basically I should never call Repo… from controller/liveview)

Or would have separate get_class!/1 and get_class_with_assoc!/1 in context?

Or am I overthinking and it doesn’t really matter? Thank you.

Most Liked

stefanchrobot

stefanchrobot

I avoid calling repo from the controllers. My usual pattern is to do this in the context:

def get_class!(id, preloads \\ []) do
  Repo.get!(Class, id)
  |> Repo.preload(preloads)
end
14
Post #2
aswinmohanme

aswinmohanme

I have a with_assoc(schema, assoc) function in each context that just passes the association directly to the Repo.preload(assoc) function.

  def with_assoc(book, assoc), do: Repo.preload(book, assoc)

I use it directly in the controller/live_view directly

    book = Books.get_book_from_slug!(book_slug) |> Books.with_assoc([:author, :draft])

When I want to use the same logic somewhere else, I create a function called get_book_from_slug_with_author_draft(book) and continue.

Gives flexibility without sacrificing readability.

benwilson512

benwilson512

Author of Craft GraphQL APIs in Elixir with Absinthe

This is a generally hard problem IMHO. One of my great open source regrets is not having enough time to make the Dataloader pattern more accessible outside of GraphQL because it was designed to address the following tension:

On the one hand: Presentation / API layers tend to want / need a lot of flexibility in terms of asking for information
On the other hand: Access control, filtering rules, and other data fetching code is often fraught with business logic, and this lives in the contexts.

If you allow the controllers to just pass in preload parameters you skip the business logic. It’s a tough problem. Two ways come to mind for how to solve this:

The dataloader pattern

Basically you create some sort of mediating entity that your business logic can hook into to enforce rules, and your “client” code can use to compose requests to fetch stuff. This works really well in Absinthe (GraphQL) because there is an actual query document to wire Dataloader into. It’s less ergonomic in traditional controllers and ends up being super callback heavy (at least today).

CQRS style Read Models

You create dedicated database views / tables where you can just query them in a simple way and the data has already been written such that it’s impossible to query data you shouldn’t see. This can quickly add a lot of boilerplate.

Where Next?

Popular in Questions Top

chokchit
** (DBConnection.ConnectionError) connection not available and request was dropped from queue after 2733ms. You can configure how long re...
New
vertexbuffer
Hello, can anybody help here..? I have a list of players and I what to delete an element, but every for loop the list is reverting to ori...
New
New
WestKeys
Currently suffering from paralysis by [HTTP client] analysis. This is rather unusual in Elixirland as there tends to be consensus on the ...
New
skosch
To my knowledge, put_in, Map.update etc. all have the one limitation of not automatically creating intermediate keys when needed (for exa...
New
jononomo
I am trying to figure out how Mix knows whether the environment is test, dev, or prod – where is this set? Thanks.
New
shahryarjb
Hello, I get Persian date from my client and convert it to normal calendar like this: def jalali_string_to_miladi_english_number(persi...
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
sergio_101
I am VERY much an elixir newbie. I have taken one elixir course and one phoenix course on Udemy. During that course, I saw the instructor...
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New

Other popular topics Top

Harrisonl
We have an ECS cluster with 4 services, where each task joins a single cluster, via discovery ECS discovery service. Currently when I de...
New
danschultzer
None of the current solutions worked well for me, so I went ahead and built a user management system from scratch. This project took far...
548 29703 241
New
AstonJ
We’ve put together this wiki for Phoenix LiveView - please feel free to add any info you feel is worth including. What is Phoenix LiveV...
New
skosch
To my knowledge, put_in, Map.update etc. all have the one limitation of not automatically creating intermediate keys when needed (for exa...
New
johnnyicon
Hi all, I’ve just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I’m trying to use Postgres...
New
stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
New
gausby
I asked this very same question on twitter and got some interesting feedback, but I thought it would be a good question to ask here as we...
1207 39523 209
New
Qqwy
Original source of discussion: This topic on the Pragmatic Programmers’ Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New
Brian
What is the proper way to load a module from a file in to IEX? In the python world, doing something like this pretty standard: from ....
New
svb
Hi! Currently I want to submit a form by pressing the Enter key. However, since my input field is of type “textarea” this is just adds a...
New

We're in Beta

About us Mission Statement