减少Django Web应用程序中的ORM查询数量

时间:2019-01-03 15:57:30

标签: python django sqlite 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 []

HistoricalData

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)

1 个答案:

答案 0 :(得分:0)

我不认为遍历查询集永远不是一个好主意。因此,如果您能找到其他方法,那就更好了。如果您可以详细说明视图功能以及应该执行的功能,那么我可以进一步提供帮助。

如果您要一个项目的所有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,这也可以完成您的工作:)