递归python函数削弱django站点性能

vwkv1x7d  于 2021-06-16  发布在  Mysql
关注(0)|答案(1)|浏览(253)

我有一个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和递归的基本性能问题,但是这是可以补救的还是需要一种根本不同的方法呢?

gdrx4gfi

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
我建议不要在您的案例中使用“自制”解决方案,因为您已经有相当多的类别需要处理,因此性能在您的案例中已经很重要了)。自己从头开始实现这些算法一点也不琐碎。

相关问题