Sen*_* H. 7 django prefetch mptt
比如说,我有一个产品实例。产品实例链接到第 4 级子类别。如果我只想获取根类别和第 4 级子类别,下面的查询足以以最少的数据库查询获取数据:
Product.objects.filter(active=True).prefetch_related('category__root',
'category')
Run Code Online (Sandbox Code Playgroud)
如果我必须联系该产品类别的父级并get_ancestors()为此使用方法,则会发生近三倍的模式数据库查询。
如果我使用get_ancestors()方法编写如下查询,则数据库查询保持低位。
Product.objects.filter(active=True).prefetch_related(
'category__root',
'category',
'category__parent',
'category__parent__parent',
'category__parent__parent__parent',
'category__parent__parent__parent__parent')
Run Code Online (Sandbox Code Playgroud)
但是当深度级别未知时,此查询无效。有没有办法在上面的查询中动态预取父母?
老问题,但我会尝试一下。
但这将需要额外的查询。(但这比可能的数百个要好。-如果不是更多的话。)
一些解释;
首先:我们需要确定活跃产品的类别深度有多少。
为了避免每次额外的查询,如果类别是 static ,您可以在启动时缓存以下代码。
max_level = Category.objects.filter(product_set__active=True)\ # Reverse lookup on product
.values('level')\
.aggregate(
max_level=models.Max('level')
)['max_level']
Run Code Online (Sandbox Code Playgroud)
第二:我们需要根据级别创建预取字符串。级别的最大数量等于父母的最大数量。
level 0 = no parents
level 1 = 1 parent
level 2 = 2 parents (nested)
level 3 = 3 parents (nested)
Run Code Online (Sandbox Code Playgroud)
这意味着我们可以轻松地循环range级别,并将父级(字符串)附加到列表中。
prefetch_string = 'category'
prefetch_list = []
for i in range(max_level):
prefetch_string += '__parent'
prefetch_list.append(prefetch_string)
Run Code Online (Sandbox Code Playgroud)
第三:我们传入prefetch_list,也将其解包。
Product.objects.filter(active=True).prefetch_related(
'category__root',
'category',
*prefetch_list) # unpack the list into args.
Run Code Online (Sandbox Code Playgroud)
然后我们可以轻松地将其重构为单个动态函数。
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
Run Code Online (Sandbox Code Playgroud)
然后你可以通过以下方式获取预取列表:
prefetch_list = get_mptt_prefetch(
'category',
related_model_qs=Category.objects.filter(product_set__active=True), # To only get categories which contain active products.
)
Run Code Online (Sandbox Code Playgroud)
https://django-mptt.readthedocs.io/en/latest/technical_details.html#level