首页 > 解决方案 > 减少 Django Web 应用程序中的 ORM 查询数量

问题描述

我正在尝试提高我的一个 Django 应用程序的性能,以使它们运行得更流畅一些,作为改进我目前正在运行的内容的第一次迭代的一部分。在进行一些分析时,我注意到我在几个页面上执行了大量的 SQL 查询。

例如,仪表板页面很容易执行 250 多个 SQL 查询。进一步调查指出我的以下代码views.py

for project in projects:
            for historicaldata in project.historical_data_for_n_months_ago(i):
                for key in ('hours', 'expenses'):
                    history_data[key] = history_data[key] + getattr(historicaldata, key)

文件中的相关函数models.py

def historical_data_for_n_months_ago(self, n=1):
    n_year, n_month = n_months_ago(n)

    try:
        return self.historicaldata_set.filter(year=n_year, month=n_month)
    except HistoricalData.DoesNotExist:
        return []

如您所见,这将导致列表中的每个项目都执行大量查询。最初是这样设置的,以将功能集中在模型级别,并在整个应用程序中引入便利功能。

在加载此页面时如何减少正在执行的查询数量有哪些可能的方法?我正在考虑删除说服功能并仅select_related()在视图中使用,但是,它仍然需要大量查询才能过滤掉给定年份和月份的记录。

提前非常感谢!

编辑根据要求,有关相关模型的更多信息。

项目

class Project(models.Model):
    name = models.CharField(max_length=200)
    status = models.IntegerField(choices=PROJECT_STATUS_CHOICES, default=1)
    last_updated = models.DateTimeField(default=datetime.datetime.now)

    total_hours = models.DecimalField(default=0, max_digits=10, decimal_places=2)
    total_expenses = models.DecimalField(default=0, max_digits=10, decimal_places=2)

    def __str__(self):
        return "{i.name}".format(i=self)

    def historical_data_for_n_months_ago(self, n=1):
        n_year, n_month = n_months_ago(n)

        try:
            return self.historicaldata_set.filter(year=n_year, month=n_month)
        except HistoricalData.DoesNotExist:
            return []

历史数据

class HistoricalData(models.Model):
    project = models.ForeignKey(Project, on_delete=models.CASCADE)
    person = models.ForeignKey(Person, on_delete=models.CASCADE)
    year = models.IntegerField()
    month = models.IntegerField()
    hours = models.DecimalField(max_digits=10, decimal_places=2, default=0)
    expenses = models.DecimalField(max_digits=10, decimal_places=2, default=0)

    def __str__(self):
        return "Historical data {i.month}/{i.year} for {i.person} ({i.project})".format(i=self)

标签: pythondjangosqliteorm

解决方案


我不认为遍历查询集是一个好主意。所以如果你能找到其他方法会更好。如果您可以详细说明您的视图功能以及它应该做什么,也许我可以提供进一步的帮助。

如果您想要项目的所有history_data 条目(反向相关),您需要使用prefetch_related。由于您需要与所述项目相关的历史数据的特定部分,因此您需要将其与 Prefetch 一起使用。

from django.db.models import Prefetch
Project.objects.prefetch_related(
    Prefetch(
        'historicaldata_set', 
        queryset=HistoricalData.objects.filter(year=n_year, month=n_month)
    )
)

之后,你应该在你的 django 模板中循环这个数据集(如果你正在使用它)。您也可以将它传递给 drf-serializer,这也可以完成您的工作:)


推荐阅读