Я пытаюсь улучшить производительность одного из моих приложений 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()
в представлении, но для фильтрации записей за данный год и месяц все равно потребуется много запросов.
Заранее большое спасибо!
Редактировать По запросу, немного больше информации о связанных моделях.
Project
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)