3

In an online shop I use django-mptt for product categories with several levels. I also have products that each belong to a category.

Now I'd like to visualise the category tree like this:

all(18)
- edible (10)
-- food (4)
-- drink (6)
- inedible (8)
-- wood (3)
-- rock (5)

where the numbers in scopes are the product counts for each category.

I am able to visualize the category tree. I can also place the product count behind the category names, but the way I am doing it seems very inefficient. I basically have a get_number_of_products() methods at the category model which returns the number of products for a category. But this needs a new database query each time.

One way to deal with this is to use caching as I don't change the product count that often, but I'd prefer a method that needs less database queries to get the tree with the product count.

Amelse Etomer
  • 1,253
  • 10
  • 28

2 Answers2

2

What you want is add_related_count

Category.objects.add_related_count(
   Category.objects.get_descendants(include_self=True),  # Queryset
   Product,  # Related mobile
   'category',  # Name of the foreignkey field
   'count',  # Name of the property added to the collection
    cumulative=True)  # Cumulative or not.

For instance in the admin you can use something like that:

class CategoryAdmin(DraggableMPTTAdmin):
    mptt_indent_field = "name"
    list_display = ('tree_actions', 'indented_title', 
                    'related_products_count', 'related_products_cumulative_count')
    list_display_links = ('indented_title',)

    def get_queryset(self, request):
        qs = super().get_queryset(request)

        # Add cumulative product count
        qs = Category.objects.add_related_count(
                qs,
                Product,
                'category',
                'products_cumulative_count',
                cumulative=True)

        # Add non cumulative recipe count
        qs = Category.objects.add_related_count(qs,
                 Product,
                 'categories',
                 'products_count',
                 cumulative=False)
        return qs

    def related_products_count(self, instance):
        return instance.products_count
    related_product_count.short_description = 'Related products (for this specific category)'

    def related_products_cumulative_count(self, instance):
        return instance.products_cumulative_count
    related_products_cumulative_count.short_description = 'Related products (in tree)'
Natim
  • 17,274
  • 23
  • 92
  • 150
  • I also added this example in the docs: http://django-mptt.readthedocs.io/en/latest/models.html#example-usage-in-the-admin – Natim Mar 22 '18 at 16:08
0

My answer is connected to Django-Oscar, but you may find it out helpful

{{ for tree category in tree_categories }} is a Category class, so it's possible get children by

{{ tree_category.get_num_children }}
  • This or the ``get_descendant_count`` gives you back the number of category the current instance is parent of but not the number of instances linked to that category. – Natim Mar 22 '18 at 14:47