python - 减少 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)
解决方案
我不认为遍历查询集是一个好主意。所以如果你能找到其他方法会更好。如果您可以详细说明您的视图功能以及它应该做什么,也许我可以提供进一步的帮助。
如果您想要项目的所有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,这也可以完成您的工作:)
推荐阅读
- java - 如果 DRL 文件中的规则超过 10 个,则 Drools 部署失败
- php - 从同一个 WordPress 数据库中的表中获取所有相关数据
- javascript - 使用 Vue.js 在测试中查找数据属性
- command-line - (MiniZinc) 无法识别的选项 `--solver'
- java - 使用 Jackson 将包含模式和数据的 XML 转换为 java 对象
- python - Python中的可编辑组合框
- c++ - Objective-C++ 不能在枚举块中使用向量 push_back
- youtrack - 如何使用 YouTrack Markdown 语法隐藏文本块?
- mongodb - 获取此错误以启动 mongo db:无法设置侦听器:SocketException:地址已在使用中
- python - Beautifulsoup 删除底部的页码