Does the use of Django template tags execute Querysets?
I thought templates are using whatever context is passed to them and no database access occurs?

Well it uses what is passed through the context. But a QuerySet is performed lazily. That means that if you for example pass MyModel.objects.all() it will not make the query, or at least not immediately.
It is only when you "consume" the queryset, for example by iterating over it, or by calculating the length of the queryset, that you will make a query to the database. A template can thus trigger several database queries. In fact N+1 problems often are caused by templates that iterate over one QuerySet of objects, and then query over a  related manager as well.
A .count() on a QuerySet is evaluated eagerly as well, so if you have somequeryset, and you call .count() that will trigger a database query. If that is done in a loop, or at different locations in the template, then it will thus reduce in more queries to the database. By using a {% with …=… %} statement, you will make the query when the template evaluates the {% with …=… %} part, and then you can reuse that variable multiple times.
I'm reading the book Django 2 by Example and am getting confused at one point there's a line {% with comments.count as total_comments %} where the author goes on to explain 

We are using the Django ORM in the template, executing the QuerySet comments.count()...The {% with %} template tag is useful to avoid
  hitting the database or accessing expensive methods multiple times.

I thought templates are using whatever context is passed to them and no database access occurs?