Как отобразить заголовок подкатегории и ее элементы под родительской категорией в django?

Я хочу отобразить название подкатегорий и их элементы в родительской категории в Django. Например, у меня есть 1 родительская категория, затем в ней 2 подкатегории, и в каждой из них по 3 подкатегории. В каждой подкатегории есть 10 элементов.

Parental category1
item1
item2

subcategory 1
item3
item4
item5

     child category 1 from subcategory1
     item6
     item7

subcategory 2
item8
item9
item10

вот мои коды:

Models.py

from django.conf import settings
from django.db import models
from django.urls import reverse
from django.utils.text import slugify
from mptt.models import MPTTModel, TreeForeignKey

class Category(MPTTModel):
  name = models.CharField(max_length=settings.BLOG_TITLE_MAX_LENGTH, unique=True)
  parent = TreeForeignKey('self', on_delete=models.CASCADE, null=True, blank=True, related_name='children')
  slug = models.SlugField(max_length=settings.BLOG_TITLE_MAX_LENGTH, null=True, blank=True)
  description = models.TextField(null=True, blank=True)
  
  class MPTTMeta:
    order_insertion_by = ['name']

  class Meta:
    verbose_name_plural = 'Categories'

  def __str__(self):
    return self.name

  def save(self, *args, **kwargs):
    value = self.name
    if not self.slug:
      self.slug = slugify(value, allow_unicode=True)
    super().save(*args, **kwargs)

  def get_absolute_url(self):
    return reverse('items-by-category', args=[str(self.slug)])

class Item(models.Model):
    title = models.CharField(max_length=settings.BLOG_TITLE_MAX_LENGTH)
    category = TreeForeignKey('Category', on_delete=models.CASCADE, null=True, blank=True)
    description = models.TextField(null=True, blank=True)    
    slug = models.SlugField(
      max_length=settings.BLOG_TITLE_MAX_LENGTH,
    )

    def __str__(self):
      return self.title

    def get_absolute_url(self):
      kwargs = {
        'slug': self.slug
      }
      return reverse('item-detail', kwargs=kwargs)

    def save(self, *args, **kwargs):
      if not self.slug:
        value = self.title
        self.slug = slugify(value, allow_unicode=True)
      super().save(*args, **kwargs)

Views.py

from django.views import generic
from .models import Item, Category


class CategoryListView(generic.ListView):
    model = Category
    template_name = "blog/category_list.html"

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['items'] = Item.objects.all()
        return context

class ItemsByCategoryView(generic.ListView):
    ordering = 'id'
    paginate_by = 10
    template_name = 'blog/items_by_category.html'

    def get_queryset(self):
        # https://docs.djangoproject.com/en/3.1/topics/class-based-views/generic-display/#dynamic-filtering
        # the following category will also be added to the context data
        self.category = Category.objects.get(slug=self.kwargs['slug'])
        queryset = Item.objects.filter(category=self.category)
         # need to set ordering to get consistent pagination results
        queryset = queryset.order_by(self.ordering)
        return queryset

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['category'] = self.category
        return context

class ItemDetailView(generic.DetailView):
    model = Item
    template_name = 'blog/item_detail.html'

items_by_category.html

{% extends "blog/base.html" %}

{% block title %}{{category.name}} list of items{% endblock %}

{% block content %}
<main>
    <section>
    <div class="container">
        <h1>{{category}} items</h1>
        <p>{{category.description}}.</p>
    </div>
    </section>
    {% include "blog/_breadcrumbs.html" with ancestors=category.get_ancestors object=category%}
    <div>
    {% for item in object_list%}
    <div>
        <a href="{{item.get_absolute_url}}">
        <p>{{item}}</p>
        </a>
    </div>
    {% endfor %}

    {% if is_paginated %}
    <div class="pagination">
        <span class="step-links">
        {% if page_obj.has_previous %}
        <a href="?page=1">&laquo; first</a>
        <a href="?page={{ page_obj.previous_page_number }}">previous</a>
        {% endif %}

        <span class="current">
            Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.
        </span>

        {% if page_obj.has_next %}
        <a href="?page={{ page_obj.next_page_number }}">next</a>
        <a href="?page={{ page_obj.paginator.num_pages }}">last &raquo;</a>
        {% endif %}
        </span>
    </div>
    {% endif %}

    </div>
</main>
{% endblock %}

Спасибо

Я пытался отобразить родительские категории, но ничего не показывает

Здесь я пытался решить подобную проблему...

clone Github repo from here

Выход браузера

enter image description here

Просматривая docs есть несколько шагов, которые вам, возможно, придется выполнить:

Во-первых, ваш набор запросов находит только одну категорию, и вам также нужны дочерние элементы этой категории. Модели MPTT имеют некоторые дополнительные функции модели, помогающие вам обойти дерево. В данном случае get_descendants уместна, поскольку мы можем сохранить исходный экземпляр в наборе. (nb: Если вам нужно все из дерева на странице, вы можете использовать get_family() вместо этого)

    self.category = Category.objects.get(slug=self.kwargs['slug'])
    #using category instance, create recordset with cat and its children
    self.category_and_children = self.category.get_descendants(include_self=True)

Теперь у нас есть полный набор запросов для поиска элементов, поэтому нам нужно адаптировать наш набор запросов

    queryset = Item.objects.filter(category__in=self.category_and_children).order_by('category')

Таким образом, здесь будут представлены все элементы, сгруппированные по категориям. Вы могли бы использовать это и легко получить плоский список, но нам нужны отступы. К сожалению, поскольку мы берем элементы, а не категории, мы не можем использовать рекурсивный шаблон MPTT для просмотра категорий и классификации дочерних элементов treeForeignKey - для этого нам придется сделать кверисет категорий и использовать prefetch_related для получения элементов, например,

    queryset = self.category_and_children.prefetch_related('item_set')

Теперь мы можем использовать что-то вроде этого в нашем шаблоне:

{% load mptt_tags %}
<ul>
    {% recursetree object.list %}
        <li>
            {{ node.name }}
            {% for item in node.item_set.all %}
                  <div>
                       <a href="{{item.get_absolute_url}}">
                       <p>{{item}}</p>
                       </a>
                  </div>                
            {% endfor %}
            {% if not node.is_leaf_node %}
                <ul class="children">
                    {{ children }}
                </ul>
            {% endif %}
        </li>
    {% endrecursetree %}
</ul>
Вернуться на верх