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.
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.