The solution of using eager loading has nothing to do with whether you run your query in the template or in code.
Anyone who claims that it's fine to run queries in templates has smoked something really good. Personally, I'll continue to aggregate all my data using JOINs before presenting them.
However due to how Django handles queries by default, calling the ORM won't actually execute the query until the queryset is evaluated in most cases (unless requesting a single object or doing an aggregation), so your queries are still technically "ran" from the template - even if you are doing prefetch_related.
I personally don't have a problem with that; explicitly casting the queryset to a list before rendering a template seems like unnecessary boilerplate and still doesn't actually isolate you from the "N+1" queries problem if your template then accesses an object's relation that hasn't been prefetched.
You'll never have to worry about whether {{ pizza.topping.name }} is hitting the database ever again, especially, if you install django-zen-queries because it will raise an exception if you try to.
for pizza in pizzas:
print(pizza.topping.name)
But you'll certainly do the following in templates: {% for pizza in pizzas %}
<p>{{ pizza.topping.name }}</p>
{% endfor %}
I also don't understand why you want to avoid 3rd party packages, especially if it helps you write better code.I'm not necessarily trying to avoid third-party packages, in fact I've installed the package you suggested in one of my projects and will be trying it out.
No, there are frameworks out there that doesn't allow queries during template rendering. See Lucky for an example, https://luckyframework.org/.