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

递归Python函数削弱Django站点性能,python,mysql,django,recursion,Python,Mysql,Django,Recursion,我有一个Django站点,它有一个类别模型,每个实例可以容纳零到n子类别 class Category(models.Model): ... parent = models.ForeignKey('self', on_delete=models.CASCADE, blank=True, null=True, related_name='subcategories') 这些类别存储在MySQL数据库中。我需要建立一个所有类别的嵌套HTML列表 class Category(mod

我有一个Django站点,它有一个
类别
模型,每个实例可以容纳零到
n
子类别

class Category(models.Model):
    ...
    parent = models.ForeignKey('self', on_delete=models.CASCADE, blank=True, null=True, related_name='subcategories')
这些类别存储在MySQL数据库中。我需要建立一个所有类别的嵌套HTML列表

class Category(models.Model):
    ...
    parent = models.ForeignKey('self', on_delete=models.CASCADE, blank=True, null=True, related_name='subcategories')
作为一种简单而肮脏的方法,我最初是通过递归函数实现的

这在一开始运行得很好,但现在有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
它产生了我想要的结果,但以牺牲效率和时间为代价


我理解Python和递归的基本性能问题,但这是可以补救的,还是需要一种根本不同的方法?

这是一个有根据的猜测:您遇到的性能问题很可能是由于访问数据库的查询数量太多,不向递归函数生成模板

subcategories=category.subcategories.all()

由于您没有使用任何预取,上面的一行将触发对您递归访问的每个类别的查询,因此,对于800个顶级类别,您将获得800个。此外,还将对每一项执行计数查询:

if subcategories.count()==0:

通过使用Django内置的模型关系急切加载,您可以稍微改进一下。 考虑一下,在关系数据库中高效地存储和查询树结构需要一些巧妙的算法。 因此,我建议使用(或至少从中汲取灵感)这个Django软件包:

Wagtail是一种流行的Django CMS,支持嵌套类别

class Category(models.Model):
    ...
    parent = models.ForeignKey('self', on_delete=models.CASCADE, blank=True, null=True, related_name='subcategories')
此包实现了三种不同的策略来存储和查询树结构:

  • 邻接表
  • 物化路径
  • 嵌套集

另一个流行的选择是:


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

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

subcategories=category.subcategories.all()

由于您没有使用任何预取,上面的一行将触发对您递归访问的每个类别的查询,因此,对于800个顶级类别,您将获得800个。此外,还将对每一项执行计数查询:

if subcategories.count()==0:

通过使用Django内置的模型关系急切加载,您可以稍微改进一下。 考虑一下,在关系数据库中高效地存储和查询树结构需要一些巧妙的算法。 因此,我建议使用(或至少从中汲取灵感)这个Django软件包:

Wagtail是一种流行的Django CMS,支持嵌套类别

class Category(models.Model):
    ...
    parent = models.ForeignKey('self', on_delete=models.CASCADE, blank=True, null=True, related_name='subcategories')
此包实现了三种不同的策略来存储和查询树结构:

  • 邻接表
  • 物化路径
  • 嵌套集

另一个流行的选择是:


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

三点意见:1。不要从python编写HTML。这就是django拥有模板的原因。2.定义最大深度。3.使用ORM返回第一个。。。Python递归并不特别慢(与Python的其余部分相比)。可能的罪魁祸首是多个数据库查询以及伴随而来的Python对象实例化。会有帮助的。但一般来说,在一个查询中获取所有类别(特别是使用
值\u list
,但这可能是一个过早的微优化),然后在Python中将它们缝合到一个层次结构中,会大大加快查询速度。@Amadan是的,似乎是大量的数据库查询在快速连续进行。三条评论:1。不要从python编写HTML。这就是django拥有模板的原因。2.定义最大深度。3.使用ORM返回第一个。。。Python递归并不特别慢(与Python的其余部分相比)。可能的罪魁祸首是多个数据库查询以及伴随而来的Python对象实例化。会有帮助的。但一般来说,在一个查询中获取所有类别(特别是使用
值\u list
,但这可能是一个过早的微优化),然后将它们缝合到Python的层次结构中,会大大加快查询速度。@Amadan是的,似乎是大量的数据库查询在快速连续进行。另一个流行的替代方案是django mptt。我集成了
django mptt
,到目前为止,性能似乎很好,因此,当我在live数据库上运行迁移时,希望这将改善情况。另一个流行的替代方案是django mptt。我集成了
django mptt
,到目前为止性能似乎很好,所以希望这将改善我在live数据库上运行迁移时的情况。