Написание эффективного метода node.all_siblings_leaf () для django -mptt - PullRequest
1 голос
/ 07 мая 2020

У меня есть система тегов, в которой теги существуют в иерархии. Я использовал django-mptt для построения модели тегов. Я хочу отображать группу тегов по-разному, если каждый брат в группе является листовым узлом.

Для этого мне нужен модельный метод с именем all_siblings_leaf(). Вот мой первый подход к этому методу:

    def all_siblings_leaf(self):
        """Return True if all siblings, including self, are leaf nodes."""
        siblings = self.get_siblings()
        return all([sibling.is_leaf_node() for sibling in siblings])

Это работает, но для вызова get_siblings() требуется только одно обращение к базе данных. Когда я хочу позвонить по номеру all_siblings_leaf(), я уже запросил все теги, которые хочу использовать. Повторное использование этого метода с набором из нескольких сотен тегов означает несколько сотен новых вызовов БД.

Поскольку у меня уже есть все теги, я чувствую, что смогу получить информацию о листовых узлах без каких-либо дополнительных поездок в db. Вот что я придумал:

    def all_siblings_leaf(self, all_tags):
        """Return True if all siblings including self are leaf nodes.

        all_tags should be a queryset that used select_related('parent')
        """
        if self.level == 0:
            siblings = [t for t in all_tags if t.level == 0]
        else:
            siblings = [t for t in all_tags if t.parent == self.parent]

        return all([sibling.is_leaf_node() for sibling in siblings])

Это работает; на странице, где этот метод используется много раз, есть только одно обращение к базе данных, измеряемое панелью инструментов отладки.

Это кажется действительно нелепым, просматривая каждый другой узел и проверяя, есть ли у него тот же родительский элемент, поскольку мы уже получили информацию о родительских элементах каждого тега. Это разумный подход или есть более простое решение? Я вызываю метод из шаблона при циклическом просмотре тегов, если это имеет значение.

1 Ответ

0 голосов
/ 25 мая 2020

Хорошо. Сначала у нас есть это ответ

Итак, ваш метод будет:

def all_siblings_is_leaf(self, include_self=False):
    siblings = self.get_siblings(include_self=include_self)
    return siblings.annotate(
        descendants_count=Floor(
            (F(self._mpttfield('right')) - F(self._mpttfield('left')) - 1) / 2
        )
    ).values(
        'descendants_count'
    ).aggregate(
        total_count=Sum('descendants_count')
    )['total_count'] == 0

Что вызывает 1 запрос БД. Для получения дополнительной информации проверьте соответствующий ответ по ссылке

...