Как отобразить заголовок подкатегории и ее элементы под родительской категорией в 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">« 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 »</a>
{% endif %}
</span>
</div>
{% endif %}
</div>
</main>
{% endblock %}
Спасибо
Я пытался отобразить родительские категории, но ничего не показывает
Просматривая 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>