Django - Эффективная выборка рекурсивной структуры categoryi
У меня есть модель, которая выглядит следующим образом:
class Category(models.Model):
name = models.CharField(max_length=50)
parent = models.ForeignKey(
'categories.Category',
null=True,
blank=True,
on_delete=models.CASCADE,
related_name='categories'
)
В основном, в поле parent
он ссылается на самого себя. Если родитель имеет значение None, то это корневая категория.
Я использую его для построения иерархии категорий.
Какой способ был бы наиболее эффективным:
- получить все объекты через иерархию
- отобразить их в шаблоне?
По какой-то причине select_related
не приводит к улучшению производительности здесь.
Я также нашел следующее: Как рекурсивно делать запросы в django эффективно?
Но мне было очень трудно применить его к моему примеру. Буду признателен за любую помощь.
Спасибо!
Вот несколько потенциальных способов оптимизации запроса и отображения иерархии категорий:
Используйте метод select_related() для предварительной выборки родительской категории для каждой дочерней категории. Это уменьшит количество запросов, необходимых для получения родительских категорий, и может повысить производительность, если вам нужно получить доступ к родительской категории для каждой дочерней категории в шаблоне:
Получение всех категорий и предварительное получение их родительских категорий
categories = Category.objects.select_related('parent')
Используйте метод prefetch_related() для предварительной выборки всех дочерних категорий для каждой родительской категории. Это уменьшит количество запросов, необходимых для получения дочерних категорий, и может повысить производительность, если вам нужно получить доступ к дочерним категориям для каждой родительской категории в шаблоне:
Получить все родительские категории и предварительно получить их дочерние категории
parent_categories = Category.objects.filter(parent__isnull=True).prefetch_related('categories')
Используйте метод Model.objects.raw() для создания необработанного SQL-запроса, который извлекает иерархию категорий в одном запросе. Это может повысить производительность, если вам нужно получить и отобразить большое количество категорий, поскольку это позволяет избежать накладных расходов на создание и инициализацию нескольких объектов модели. Вот пример необработанного SQL-запроса, который извлекает иерархию категорий:
Создайте необработанный SQL-запрос, который извлекает иерархию категорий
raw_query = """
SELECT c.id, c.name, c.parent_id
FROM categories_category c
WHERE c.parent_id IS NULL
UNION ALL
SELECT c.id, c.name, c.parent_id
FROM categories_category c
INNER JOIN (
SELECT c.id, c.name, c.parent_id
FROM categories_category c
WHERE c.parent_id IS NOT NULL
) child_categories ON child_categories.parent_id = c.id
"""
Выполните необработанный SQL-запрос и получите иерархию категорий
categories = Category.objects.raw(raw_query)
В шаблоне можно использовать рекурсивный тег шаблона для отображения иерархии категорий. Вот пример того, как это сделать:
{% load recursive_tags %}
<ul>
{% recursetree categories %}
<li>
{{ node.name }}
{% if not node.is_leaf_node %}
<ul>
{{ children }}
</ul>
{% endif %}
</li>
{% endrecursetree %}
</ul>
Обратите внимание, что библиотека django-mptt должна быть установлена и загружена, чтобы использовать тег шаблона recursetree. Обратитесь к документации для получения дополнительной информации о том, как использовать рекурсивные шаблонные теги.
Одним из способов эффективного получения и отображения иерархической структуры категорий в Django с помощью PostgreSQL является использование пакета django-mptt. Этот пакет предоставляет реализацию Modified Preorder Tree Traversal, который является эффективным алгоритмом для представления иерархических структур в базе данных.
Используя django-mptt, вы определите вашу модель Category следующим образом:
from mptt.models import MPTTModel, TreeForeignKey
class Category(MPTTModel):
name = models.CharField(max_length=50)
slug = models.SlugField()
parent = TreeForeignKey(
'self',
null=True,
blank=True,
on_delete=models.CASCADE,
related_name='categories'
)
Это добавит в вашу модель несколько полей, таких как left, right, tree_id и level, которые используются алгоритмом django-mptt для представления иерархии.
Чтобы получить все объекты в иерархии, вы можете использовать метод get_descendants, предоставляемый django-mptt:
root_category = Category.objects.get(parent=None)
all_categories = root_category.get_descendants(include_self=True)
Это вернет набор запросов, содержащий все категории в иерархии, начиная с корневой категории.
Для отображения категорий в шаблоне можно использовать теги шаблона mptt, предоставляемые django-mptt:
{% load mptt_tags %}
<ul>
{% recursetree all_categories %}
<li>
{{ node.name }}
{% if not node.is_leaf_node %}
<ul class="children">
{{ children }}
</ul>
{% endif %}
</li>
{% endrecursetree %}
</ul>
Это отобразит иерархическую структуру категорий в формате вложенного списка, с отступом от каждого уровня и стилями в соответствии со стилями mptt по умолчанию.
В целом, использование django-mptt является более эффективным и удобным способом представления и получения иерархических структур в Django по сравнению с использованием рекурсивных SQL-запросов.
Одним из возможных решений может быть использование https://django-mptt.readthedocs.io/en/latest/overview.html#what-is-django-mptt
MPTT - это техника хранения иерархических данных в базе данных. Цель цель - сделать операции поиска очень эффективными. Компромиссом за эту эффективность является то, что выполнение вставок и перемещение элементов по дереву более трудоемко, поскольку требуется дополнительная работа для поддержания структуры дерева в хорошем состоянии в любое время.
from django.db import models from mptt.models import MPTTModel, TreeForeignKey class Category(MPTTModel): name = models.CharField(max_length=50) slug = models.SlugField() parent = TreeForeignKey( 'self', null=True, blank=True, on_delete=models.CASCADE, related_name='children' ) class MPTTMeta: order_insertion_by = ['name']
Вы можете использовать тег шаблона django-mptt следующим образом:
{% load mptt_tags %} <ul> {% recursetree categories %} <li> {{ node.name }} {% if not node.is_leaf_node %} <ul class="children"> {{ children }} </ul> {% endif %} </li> {% endrecursetree %} </ul>
Имеется учебник и дополнительная информация в библиотеке docs.