Django/Wagtail - 通过多个 parent 页面优化页面查询集的最有效方法
Django/Wagtail - most efficient way to refine page queryset by multiple parent pages
我得到的分类页面列表如下:
category_ids = request.GET.getlist('category[]')
category_pages = MyCategoryPage.objects.filter(pk__in=category_ids)
我想做的是优化页面列表,使其仅包含属于上述 children 类别的页面。
我知道您可以按如下方式为一个 object 执行此操作:
pages = MySubPage.objects.child_of(category)
..但据我所知,没有办法 child_of(category_pages)
超过一个 parent.
在正常的 Django 中,我可能在 MySubPage
上有一个类别 ForeignKey
,我会这样做:
pages = MySubPage.objects.filter(category__in=filter_categories)
..但我还是不确定我能否在 Wagtail 中做到这一点。
我知道我可能可以遍历所有类别并将子页面附加到列表中:
pages = []
for category in category_pages:
pages.append(MySubPage.objects.child_of(category))
...然后加入它们,但这似乎不是很有效,显然它会 return 一个列表而不是一个查询集。
我已经搜索了文档,但找不到更直接的方法来实现这一点。
要解决这个问题,您可以简单地查看在查询集上调用 child_of
时后台发生的情况。这是它使用的这个方法和这个查询集的方法的样子(它们在代码中的出现顺序未保留):
def child_of(self, other):
"""
This filters the QuerySet to only contain pages that are direct children of the specified page.
"""
return self.filter(self.child_of_q(other))
def child_of_q(self, other):
return self.descendant_of_q(other) & Q(depth=other.depth + 1)
def descendant_of_q(self, other, inclusive=False):
q = Q(path__startswith=other.path) & Q(depth__gte=other.depth)
if not inclusive:
q &= ~Q(pk=other.pk)
return q
如您所见,wagtail 只会 return 一个带有一些 Q
对象的查询集来过滤页面列表。此过滤器基于父页面的路径和深度。要重新创建相同的过滤器,但有多个父项,您可以执行以下操作:
import operator
category_ids = request.GET.getlist('category[]')
pages = MySubPage.objects.filter(reduce(
operator.or_,
(
Q(path__startswith=category.path) & Q(depth=category.depth + 1)
for category in MyCategoryPage.objects.filter(pk__in=category_ids)
)
))
请注意,我通过从中删除 Q(depth__gte=category.depth)
来优化查询,因为 Q(depth=category.depth + 1)
将满足此条件。出于同样的原因,它也不需要 ~Q(pk=category.pk)
。
另请注意,这总共需要 2 个查询,可以直接在 GET
参数中巧妙地使用 path
和 depth
类别,将其优化为仅一个查询。
我得到的分类页面列表如下:
category_ids = request.GET.getlist('category[]')
category_pages = MyCategoryPage.objects.filter(pk__in=category_ids)
我想做的是优化页面列表,使其仅包含属于上述 children 类别的页面。
我知道您可以按如下方式为一个 object 执行此操作:
pages = MySubPage.objects.child_of(category)
..但据我所知,没有办法 child_of(category_pages)
超过一个 parent.
在正常的 Django 中,我可能在 MySubPage
上有一个类别 ForeignKey
,我会这样做:
pages = MySubPage.objects.filter(category__in=filter_categories)
..但我还是不确定我能否在 Wagtail 中做到这一点。
我知道我可能可以遍历所有类别并将子页面附加到列表中:
pages = []
for category in category_pages:
pages.append(MySubPage.objects.child_of(category))
...然后加入它们,但这似乎不是很有效,显然它会 return 一个列表而不是一个查询集。
我已经搜索了文档,但找不到更直接的方法来实现这一点。
要解决这个问题,您可以简单地查看在查询集上调用 child_of
时后台发生的情况。这是它使用的这个方法和这个查询集的方法的样子(它们在代码中的出现顺序未保留):
def child_of(self, other):
"""
This filters the QuerySet to only contain pages that are direct children of the specified page.
"""
return self.filter(self.child_of_q(other))
def child_of_q(self, other):
return self.descendant_of_q(other) & Q(depth=other.depth + 1)
def descendant_of_q(self, other, inclusive=False):
q = Q(path__startswith=other.path) & Q(depth__gte=other.depth)
if not inclusive:
q &= ~Q(pk=other.pk)
return q
如您所见,wagtail 只会 return 一个带有一些 Q
对象的查询集来过滤页面列表。此过滤器基于父页面的路径和深度。要重新创建相同的过滤器,但有多个父项,您可以执行以下操作:
import operator
category_ids = request.GET.getlist('category[]')
pages = MySubPage.objects.filter(reduce(
operator.or_,
(
Q(path__startswith=category.path) & Q(depth=category.depth + 1)
for category in MyCategoryPage.objects.filter(pk__in=category_ids)
)
))
请注意,我通过从中删除 Q(depth__gte=category.depth)
来优化查询,因为 Q(depth=category.depth + 1)
将满足此条件。出于同样的原因,它也不需要 ~Q(pk=category.pk)
。
另请注意,这总共需要 2 个查询,可以直接在 GET
参数中巧妙地使用 path
和 depth
类别,将其优化为仅一个查询。