Альтернатива использованию `regroup` в шаблоне Django на `ListView`, использующем много памяти
Я пробовал использовать следующее для перегруппировки набора запросов ListView
в моем шаблоне, чтобы объекты группировались по значению связанного поля:
{% regroup object_list by related_field.sort_value as grouped_list %}
{% for group in grouped_list %}
<span>{{group.grouper}}</span>
{% for item in group.list %}
<p>{{item.related_field.name}}</p>
<p>{{item.description}}</p>
{% endfor %}
{% endfor %}
В модели около десятка полей, два из них реляционные (первая связанная модель имеет пару десятков полей, другая - всего несколько), и всего в результатах кверисета ListView
несколько сотен объектов.
Просмотр этого на локальной/деловой машине не вызывает проблем, но при просмотре на производственном Heroku приложение падает из-за чрезмерного использования памяти (~2GB). В настоящее время приложение работает на Hobby (512 МБ), и хотя я, вероятно, увеличу его до Standard 1x/2x, это все равно будет мало памяти, если я не перейду на более высокую производительность dyno, что является излишеством, за исключением этого единственного представления.
Что я могу сделать, чтобы уменьшить использование памяти этим представлением?
Для начала вы обращаетесь к related_field
(related_model_b
/related_model_a
?) для каждого элемента вашего кверисета, который каждый раз выполняет запрос к БД, используйте select_related()
для получения связанных данных в одном запросе
MyModel.objects.select_related('related_model_b', 'related_model_a').order_by(...)
Поскольку вы уже сортируете по related_field.sort_value
, вы можете обойтись использованием тега ifchanged
вместо regroup
, если regroup
представляет собой большую проблему
{% for item in object_list %}
{% ifchanged item.related_field.sort_value %}<span>{{ item.related_field.sort_value }}</span>{% endifchanged %}
<p>{{item.related_field.name}}</p>
<p>{{item.description}}</p>
{% endfor %}
В итоге я использовал комбинацию из предложения Iain использовать selected_related
, а также defer()
для исключения некоторых полей в моих связанных моделях, которые не нужны для этого представления.
В частности, я считаю (но не проверял), что виновником является JSONField
, прикрепленный к связанной модели, поскольку это поле обычно вмещает всего пару кБ данных, но в некоторых случаях оно вмещает ~400 кБ.
Я изменил набор запросов в моем представлении на следующий:
class BigListView(ListView):
model = MyModel
template_name = 'my_model/big_list.html'
def get_queryset(self):
return MyModel.objects.select_related('related_model_a','related_model_b').order_by('related_model_b__sort_value').defer(
'related_model_b__json_data',
... additional fields ...
)
- Оригинальное использование памяти составляло почти 2 гБ.
- Использование
select_related
только уменьшило это значение до ~800 мБ. - Использование
select_related
иdefer()
уменьшило его до ~200 мБ, что не отличается от того, что приложение обычно использует для любой операции.