虽然这个问题早已有人问过,但我会尽力回答。
不过这需要进行额外的查询。(但这比可能出现的数百次查询要好——如果不是更多的话。)
一些解释:
首先:我们需要确定活跃产品所属类别的深度。
为了避免每次都进行额外的查询,如果类别是静态的,你可以在启动时缓存以下代码。
max_level = Category.objects.filter(product_set__active=True)\ # Reverse lookup on product
.values('level')\
.aggregate(
max_level=models.Max('level')
)['max_level']
第二步:我们需要根据级别创建预取字符串。最大级别数量等于最大父级数量。
level 0 = no parents
level 1 = 1 parent
level 2 = 2 parents (nested)
level 3 = 3 parents (nested)
这意味着我们可以轻松地循环遍历级别的
范围
,并将父项(字符串)添加到列表中。
prefetch_string = 'category'
prefetch_list = []
for i in range(max_level):
prefetch_string += '__parent'
prefetch_list.append(prefetch_string)
第三步:我们传入prefetch_list
,同时对其进行解包。
Product.objects.filter(active=True).prefetch_related(
'category__root',
'category',
*prefetch_list)
我们可以将这个内容轻松地重构成一个单一的动态函数。
def get_mptt_prefetch(field_name, lookup_name='__parent', related_model_qs=None):
max_level = related_model_qs\
.values('level')\
.aggregate(
max_level=models.Max('level')
)['max_level']
prefetch_list = []
prefetch_string = field_name
for i in range(max_level):
prefetch_string += lookup_name
prefetch_list.append(prefetch_string)
return prefetch_list
然后,您可以使用以下方法获取预取列表:
prefetch_list = get_mptt_prefetch(
'category',
related_model_qs=Category.objects.filter(product_set__active=True), # To only get categories which contain active products.
)
https://django-mptt.readthedocs.io/en/latest/technical_details.html#level