Подсчет всех элементов в узлах django-treebeard Materialised Path
Я использую django-treebeard для создания дерева Категорий, каждая из которых содержит несколько Книг:
from django.db import models
from treebeard.mp_tree import MP_Node
class Book(models.Model):
title = models.CharField(max_length=255, blank=False, null=False)
categories = models.ManyToManyField("Category", related_name="books", blank=True)
class Category(MP_Node):
title = models.CharField(max_length=255, blank=False, null=False)
book_count = models.IntegerField(default=0, blank=False,
help_text="Number of Books in this Category")
total_book_count = models.IntegerField(default=0, blank=False,
help_text="Number of Books in this Category and its descendants")
Я хочу отобразить количество книг в каждой категории, хранящихся в Category.book_count
, поэтому у меня есть сигнал post_save
следующего вида:
from django.dispatch import receiver
@receiver(post_save, sender=Book)
def book_post_save(sender, **kwargs):
for category in kwargs["instance"].categories.all():
category.book_count = category.websites.count()
category.save()
(В реальности все немного сложнее, но суть такова)
Это работает отлично, и я также могу сделать подобное для сигнала m2m_changed
, если категории книги изменились.
НО, я также хочу подсчитать total_book_count
- общее количество книг в категории и во всех ее потомках. Я могу сделать это с помощью метода, подобного следующему на Category
:
def set_total_book_count(self):
# All the Books in this Category's descendants:
descendant_books = Book.objects.filter(categories__in=self.get_descendants())
# Combine with the Books in this Category but avoid counting duplicates:
books = (descendant_books | self.books).distinct()
self.total_book_count = books.count()
Это тоже работает, и я могу вызывать его из сигналов. НО все это становится сложным, особенно если я перемещаю категорию - мне придется пересчитать total_book_count
для перемещенной категории, всех ее предыдущих предков и всех ее новых предков. В этот момент, возможно, проще пересчитать подсчеты для ВСЕХ категорий... процесс, который имеет свои трудности (например, с чего начать пересчет).
Другие люди наверняка уже делали что-то подобное, и поэтому я задаюсь вопросом, не изобретаю ли я заново колесо? Есть ли существующие реализации такого рода вещей?
Это частичный ответ, поскольку это простой способ пересчета всех подсчетов для всех категорий. Но я ожидаю, что он может оказаться медленным, если есть много категорий и много книг, учитывая, что set_total_book_count()
нужно сделать для каждой категории.
categories = Category.get_tree()
for category in categories:
category.book_count = category.books.count()
category.set_total_book_count()
category.save()