How, when selecting a category, display both the elements of this category and the elements of all its subcategories?

I am new to django, and in python too, now I am practicing on creating an online store and faced the problem that it would be logical, when choosing a category, to also display products from all its subcategories. Google, unfortunately, did not help … I think that you need to somehow correctly filter the product in the views, but how? skills are not enough to understand. Using Django-mptt for the category tree (fewer database queries)

models.py

class Category(MPTTModel):
    name = models.CharField(max_length=200, db_index=True)
    slug = models.SlugField(max_length=200, db_index=True, unique=True)
    parent = TreeForeignKey('self', on_delete=models.CASCADE, null=True, blank=True, related_name='children')

class MPTTMeta:
    order_insertion_by = ['name']

    class Meta:
        ordering = ('name',)
        verbose_name = 'Категория'
        verbose_name_plural = 'Категории'

    def get_absolute_url(self):
        return reverse('shop:product_list_by_category', args=[self.slug])

    def __str__(self):
        return self.name

views.py

def product_list(request, category_slug=None):
    category = None
    categories = Category.objects.all()
    products = Product.objects.filter(available=True).order_by('-created')
    if category_slug:
        category = get_object_or_404(Category, slug=category_slug)
        products = products.filter(category=category)
    return render(request,
                  'shop/shop.html',
                  {'category': category,
                  'categories': categories,
                   'products': products})

html

<div class="category_conteiner">
{% recursetree categories %}
    <div class="sub_menu_category">
        <a href="{{ node.get_absolute_url }}" class="subcatbtn">{{ node.name }}</a>
        {% if not node.is_leaf_node %}
        <div class="subcat_content">
            <div class="subcat_column">
                {{ children }}
            </div>
        </div>
        {% endif %}
    </div> 
{% endrecursetree %}

For example, there are such categories:
-Clothing
— Jackets
— Hats

If the product is in the “Jackets” category and we go into this category, everything is fine, but if we go to “Clothes” then it will be empty, but you need to have all the products from both the “Jackets” category and from the “Hats”

Now the sorting of products is obtained only by the category that was selected when adding the product

Answer

You can do it in an extra step. You need to get child categories and then filter products by them. Example code:

category = Category.objects.get(category_slug=category_slug)
sub_categories = category.get_descendants(include_self=True)
Product.objects.filter(category__in=sub_categories)

And here is the full code for your case.

def product_list(request, category_slug=None):
    category = None
    categories = Category.objects.all()
    products = Product.objects.filter(available=True).order_by('-created')
    if category_slug:
        category = get_object_or_404(Category, slug=category_slug)
        sub_categories = category.get_descendants(include_self=True)
        products = products.filter(category__in=sub_categories)
    return render(request,
                  'shop/shop.html',
                  {'category': category,
                  'categories': categories,
                   'products': products})