我有一个django网站 Category
模型,每个示例可以容纳0到 n
子类别。
class Category(models.Model):
...
parent = models.ForeignKey('self', on_delete=models.CASCADE, blank=True, null=True, related_name='subcategories')
这些类别存储在mysql数据库中。我需要建立一个所有类别的嵌套html列表。
作为一种简单而肮脏的方法,我最初是通过一个递归函数来实现的。
这在一开始效果不错,但现在有800多个类别,这导致请求速度急剧下降。运行开发服务器时,每次至少需要60秒。
下面是函数,稍微简化一下:
def get_category_map(categories, root=False):
category_map = ''
if categories:
if root:
category_map += '<ul id="root">'
else:
category_map += '<ul>'
for category in categories:
category_map += '<li>'
subcategories = category.subcategories.all()
if subcategories.count() == 0:
category_map += '<a class="category-link" href="' + reverse('my_app:category_page', args=(category.pk, category.slug)) + '">' + category.title + '</a>'
else:
category_map += '<span class="category-drop-down">' + category.title + '</span>'
# Recursive call here cripples performance.
category_map += get_category_map(subcategories)
category_map += '</li>'
category_map += '</ul>'
return category_map
函数的初始调用如下:
get_category_map(Category.objects.filter(parent=None), root=True)
它产生了我想要的结果,但以牺牲效率和时间为代价。
我理解python和递归的基本性能问题,但是这是可以补救的还是需要一种根本不同的方法呢?
1条答案
按热度按时间gdrx4gfi1#
这是一个有根据的猜测:您遇到的性能问题很可能是由于命中数据库的查询数,而不是由于生成模板的递归函数。
subcategories = category.subcategories.all()
由于您不使用任何预取,上面的一行将触发对您递归访问的每个类别的查询,因此对于800个顶级类别,您将得到800个。此外,您还将对每个查询执行计数查询:if subcategories.count() == 0:
通过使用django内置的模型关系的急切加载,您可以改进一些。考虑一下,在关系数据库中高效地存储和查询树结构需要一些聪明的算法。因此,我建议使用(或至少从中获得灵感)这个django软件包:https://django-treebeard.readthedocs.io/en/latest/
wagtail是一种流行的django cms,它支持嵌套类别。
此包实现了三种不同的策略来存储和查询树结构:
邻接表
或原料路径
嵌套集
另一个流行的选择是:
https://github.com/django-mptt/django-mptt
我建议不要在您的案例中使用“自制”解决方案,因为您已经有相当多的类别需要处理,因此性能在您的案例中已经很重要了)。自己从头开始实现这些算法一点也不琐碎。