Bac*_*con 15 python django mptt django-mptt hierarchical-data
有没有人有一个有效的算法来检索一个mptt查询集的所有祖先?到目前为止我能想到的最好的是这样的:
def qs_ancestors(queryset):
if isinstance(queryset, EmptyQuerySet):
return queryset
queryset_aggs = queryset.values_list('tree_id', 'level').annotate(max_lft=Max('lft'), min_rght=Min('rght'))
new_queryset = queryset.none()
for tree_id, level, max_lft, min_rght in queryset_aggs:
ancestors = MyModel.objects.filter(
tree_id=tree_id,
level__lt=level,
lft__lte=max_lft,
rght__gte=min_rght,
)
new_queryset = ancestors | new_queryset
return new_queryset
Run Code Online (Sandbox Code Playgroud)
这种方法存在两个问题:
number_of_trees*number_of_levels在最终查询中有条款,它可以非常快地变得非常大我愿意在其他地方缓存祖先,但我想不出有效的方法.我考虑添加一个字段,用逗号分隔的祖先id的列表,然后GROUP_CONCAT在一个额外的内部做一个(我在MySQL中),但我认为这可能会变得很大/很慢.
我不得不写一次类似的算法。我有一个显示 MPTT 树的视图,它是一棵非常大的树,所以我无法在 HTML 模板中加载它的所有数据。所以我在初始加载时只显示根节点,并使用 Ajax 加载其他节点。
它运行良好,直到我的老板要求我实施“搜索”选项。搜索必须查看所有节点并在找到匹配项时分解树。我花了一段时间才弄明白这一点,但我终于明白了。这是一个想出的解决方案:
from django.db.models import Q
def get_parents(self, qs):
tree_list = {}
query = Q()
for node in qs:
if node.tree_id not in tree_list:
tree_list[node.tree_id] = []
parent = node.parent.pk if node.parent is not None else None,
if parent not in tree_list[node.tree_id]:
tree_list[node.tree_id].append(parent)
query |= Q(lft__lt=node.lft, rght__gt=node.rght, tree_id=node.tree_id)
return YourModel.objects.filter(query)
Run Code Online (Sandbox Code Playgroud)
它只需要运行两个查询,qs作为参数传递的初始查询和函数返回的最终查询集。这tree_list是一个存储已经添加到查询集中的节点的字典,它是一种优化,算法不需要工作。但是由于我正在处理一棵相对较大的树,因此我必须将其包含在内。
我想你可以把这个方法变成一个管理器,让它更通用,即让它适用于任何 MPTT 模型,而不仅仅是 YourModel
怎么样:
def qs_ancestors(queryset):
if isinstance(queryset, EmptyQuerySet):
return queryset
new_queryset = queryset.none()
for obj in queryset:
new_queryset = new_queryset | obj.get_ancestors()
return new_queryset
Run Code Online (Sandbox Code Playgroud)
它仍然是 len(queryset) 子句。您可以通过执行预处理过程来删除查询集中其他对象的祖先对象,从而减少子句的数量,例如:
min_obj_set = []
for obj in queryset.order_by('tree_id', '-level'):
for obj2 in min_obj_set:
if obj.is_ancestor_of(obj2):
break
else:
min_obj_set.append(obj)
Run Code Online (Sandbox Code Playgroud)
尽管上面的代码片段只是一个示例,但如果您的查询集包含大量对象,您可能需要使用 BST。
不过,您必须测试与较大的数据库查询相比,这是否会提高速度。
| 归档时间: |
|
| 查看次数: |
2981 次 |
| 最近记录: |