Django-mptt get_descendants для списка узлов
Я пытаюсь получить все descendants(include_self=True)
не для одного
Node, но для списка (QuerySet) узлов. Это должен быть один SQL
запрос.
Пример (что на самом деле не работает:)
some_nodes = Node.objects.filter( ...some_condition... )
some_nodes.get_descendants(include_self=True) #hopefully I would like
to have all possible Nodes starting from every node of "some_nodes"
Единственная идея, которую я имею прямо сейчас, - это итерация через some_nodes и
запустите get_descendants() для каждого node - но это ужасное решение
(много SQL-запросов).
Если нет чистого способа сделать это через Django ORM, вы можете предоставить мне пользовательский
SQL для запуска вместо этого? Здесь вы можете сделать предположение, что у меня есть список
Node pk.
EDIT: если это может помочь - все мои "some_nodes" помещаются в один и тот же родительский каталог и имеют одинаковый "уровень" в дереве.
Ответы
Ответ 1
Огромное спасибо Крейгу де Стигтеру ответил на мой вопрос о группе django-mptt-dev, на случай, если кому-то это понадобится. Я любезно перепробовал его решение http://groups.google.com/group/django-mptt-dev/browse_thread/thread/637c8b2fe816304d
from django.db.models import Q
import operator
def get_queryset_descendants(nodes, include_self=False):
if not nodes:
return Node.tree.none()
filters = []
for n in nodes:
lft, rght = n.lft, n.rght
if include_self:
lft -=1
rght += 1
filters.append(Q(tree_id=n.tree_id, lft__gt=lft, rght__lt=rght))
q = reduce(operator.or_, filters)
return Node.tree.filter(q)
Пример Node дерево:
T1
---T1.1
---T1.2
T2
T3
---T3.3
------T3.3.3
Пример использования:
>> some_nodes = [<Node: T1>, <Node: T2>, <Node: T3>] # QureySet
>> print get_queryset_descendants(some_nodes)
[<Node: T1.1>, <Node: T1.2>, <Node: T3.3>, <Node: T3.3.3>]
>> print get_queryset_descendants(some_nodes, include_self=True)
[<Node: T1>, <Node: T1.1>, <Node: T1.2>, <Node: T2>, <Node: T3>, <Node: T3.3>, <Node: T3.3.3>]
Ответ 2
В более поздних версиях mptt уже эта функция встроена в диспетчер объектов. Таким образом, решение этой проблемы заключается в следующем:
Node.objects.get_queryset_descendants(my_queryset, include_self=False)
Ответ 3
Django mptt использует измененный метод обхода дерева по порядку, как описано в Документ управления MySQL иерархическими данными.
Он имеет следующий запрос для возврата всех узлов в дереве ниже определенного node:
SELECT node.name
FROM nested_category AS node, nested_category AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
AND parent.name = 'ELECTRONICS'
ORDER BY node.lft;
Секрет - это номера parent.lft и parent.rgt, все дети будут иметь значение node.lft между ними.
Очевидно, что этот пример предполагает только одного родителя и что вам нужно использовать родительское имя для поиска родителя. Поскольку у вас есть родительские данные node, вы можете сделать что-то вроде следующего:
SELECT node.id
FROM node_table
WHERE node.lft BETWEEN parent[0].lft AND parent[0].rgt
OR node.lft BETWEEN parent[1].lft AND parent[1].rgt
Я оставлю это как упражнение для вас, как создать отдельное предложение BETWEEN для каждого родителя node (подсказка, "AND".join)
В качестве альтернативы вы можете использовать генератор диапазонов для каждого родителя, чтобы получить все значения между всеми родительскими значениями lft и rgt включительно. Это позволяет использовать гигантский оператор IN, а не много предложений BETWEEN.
Объедините оба из вышеперечисленного с RawQueryset, и вы вернете модели.