Альтернатива использованию `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 ...
        )
  1. Оригинальное использование памяти составляло почти 2 гБ.
  2. Использование select_related только уменьшило это значение до ~800 мБ.
  3. Использование select_related и defer() уменьшило его до ~200 мБ, что не отличается от того, что приложение обычно использует для любой операции.
Вернуться на верх