Run queries inside Views?

I have some <select> elements in my application that are used in multiple places, like a list of countries or currencies.

Right now I’m loading the data in the controller and injecting into the template via render, but it’s a lot of repeated work as I need to load the data in every new/create/edit/update actions. Defining a plug for those actions would ease the work, but again I would need to do it for every controller.

Would be ok to break the rule of “never run queries from the view side” and create view helpers like country_select and currency_select loading data inside them? Something like

def country_select do
  countries = Countries.list_countries

  ...
end
1 Like

What forbids you from defining these helpers in the domain layer aka “contexts”?

Oh I see, they are html-specific … Then I think it’s ok.

1 Like

Yes they’re HTML specific. I just edited to make it more clear (hopefully).

The Countries.list_countries/0 in the example is my context. The view helper would call it to load all countries and return a list of tuples for populating the HTML select options.

I’m personally of the mind to never run queries in a view, it would make it a ‘hidden cost’.

You could make helpers to make passing it into a render better (you can even inject your own render helpers into your controllers, but then that starts to ‘hide the cost again’.

Honestly for something like Countries I’m guessing it does not change all that often, I’d probably just use Cachex or so (with a fallback to the database) to grab the data so it is not calling the database often, then call that from the view (via a helper of course). If the data changes in the database just tell Cachex to clear and re-acquire it (or just put in the new values directly) and can link to a phoenix pubsub for multi-system clearing on updates too.

1 Like

From what I see in my code, such one-per-page forms tend to turn into one-per-item-on-page situations, which is hard to refactor later on.

Plugs are the way to go imho.

2 Likes

@luizdamim We’ve wrestled with this question recently as well as we initially were doing some of this in view functions but as others mentioned these costs get hidden and its easy to end up with N+1 queries. Recently we began moving the data fetching functionality out of views and into controllers; we still format the option tuples in a view function, but we pass that an assign with the data.

@Kham do you mean you write a plug that fetches and assigns these values so that each controller function doesn’t have to embed that logic? I think is a good suggestion for cases where multiple views will need the same option lists.

Thank you @OvermindDL1 and @Kham, you both are right.

I’ll keep loading the data in the controllers and passing to templates while I think something else.

1 Like