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

标签 python mysql django recursion

我有一个带有 Category 模型的 Django 网站,每个实例都可以容纳零到 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 和递归的基本性能问题,但是这可以挽救吗,还是需要一种根本不同的方法?

最佳答案

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

子类别=category.subcategories.all()

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

如果 subcategories.count() == 0:

您可以通过使用 Django 内置的模型关系预加载来改进一些事情。 考虑一下,在关系数据库中有效地存储和查询树结构需要一些聪明的算法。 所以我建议使用(或者至少从中汲取灵感)这个 Django 包:

https://django-treebeard.readthedocs.io/en/latest/

它由 Wagtail 使用,Wagtail 是一种支持嵌套类别的流行 Django CMS。

该包实现了三种不同的策略来存储和查询树结构:

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

另一种流行的替代方案是:

https://github.com/django-mptt/django-mptt

我建议不要在您的情况下使用“自制”解决方案,因为您已经有相当多的类别需要处理,因此性能在您的情况下已经很重要)。从头开始自己实现这些算法绝非易事。

关于python - 递归 Python 函数削弱 Django 站点性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54036266/

相关文章:

python - 在 Pandas DataFrame 中构建复杂的子集

python - Django makemessages 不创建新添加的语言

django - 在 django 中保存 MultipleChoiceField

python - 运行关键字/函数的最有效方法是什么?

python - 有没有办法在不破坏函数链的情况下在 PySpark 中执行强制转换或 withColumn 数据帧操作?

python - AWS Python Shell - 如何使用 Glue 目录连接

php - MySQL:按子句分组

如果 doc 的关键字发生变化,则 MySQL 会触发 doc 的更新时间戳

python - Flask 应用程序从 mysql 连接拒绝访问

python - Django 部署。加载 MySQLdb 模块时出错。从/tmp 目录读取/写入时出现问题