递归Python函数削弱Django网站性能

时间:2019-01-04 09:34:28

标签: python mysql django recursion

我有一个带有Category模型的Django站点,该站点的每个实例可以容纳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 个答案:

答案 0 :(得分:2)

这是一个有根据的猜测:您遇到的性能问题很可能是由于命中数据库的查询数量所致,而不是由于生成模板的递归函数所致。

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

在您的情况下,我建议您不要使用“自制”解决方案,因为您已经有很多类别要处理,因此性能对于您的情况已经很重要。从头开始自己实现这些算法并不是一件容易的事。