Прежде всего, если используете иерархические структуры данных, то используйте их с
django-mptt. Во-первых, это позволит работать с любым уровнем вложенности, в том числе неопределённым на этапе разработки и переменным в разных ветках. Во-вторых, это облегчит вам работу, предоставив простые и эффективные методы манипуляции ветками. Наконец, это позволит получать данные из БД наиболее быстрым и эффективным способом, с минимальной затратой ресурсов.
Конкретно по вашей задаче решением в лоб было бы определить в модели свойство, возвращающее количество объявлений в дочерних категориях:
class Category(models.Model):
parent = TreeForeignKey('self', verbose_name=u'Родитель', null=True, blank=True, related_name='children')
title = models.CharField(u'Название', max_length=100)
@property
def items_count(self):
# получаем список идентификторов всех низлежащих категорий, включая интересующую нас
ids = self.get_descendants(include_self=True).values_list('id')
# возвращаем количество товаров, имеющих родителем категорию с идентификатором входящим
# в список полученный строкой выше
return Product.objects.filter(parent_id__in=ids).count()
и потом вывести их в шаблоне как-то так
<ul class="root">
{% recursetree nodes %}
<li>
{{ node.title }}<span>{{ note.items_count }}</span>
{% if not node.is_leaf_node %}
<ul class="children">
{{ children }}
</ul>
{% endif %}
</li>
{% endrecursetree %}
</ul>
Но беда в том, что это квадратичный алгоритм, чем больше будет категорий отображаться на странице, тем больше запросов будет к базе при открытии этой страницы. Я так и не нашёл в Django лучшего решения для этого, чем делать сырой SQL запрос к базе:
SELECT name, cp.p_count FROM catalog_category AS cc
INNER JOIN LATERAL (
SELECT cc.id AS id, SUM(products) AS p_count FROM (
SELECT cc.id AS parent_id, category_id, COUNT(id) AS products
FROM catalog_product
WHERE category_id IN (
SELECT id FROM catalog_category
WHERE lft <= cc.rght AND lft >= cc.lft AND tree_id = cc.tree_id)
GROUP BY category_id
) AS sub_cс
GROUP BY parent_id
) AS cp
USING(id) ORDER BY name;