Представление Django работает медленно

У меня есть представление в моем проекте Django:

@permission_required('storage.add_iteminrelease')
@transaction.atomic
def add_item_in_release(request, id):
    release = get_object_or_404(Release, pk=id)
    if (request.method == 'POST'):
        add_item_in_release_form = ItemInReleaseForm(request.POST)
        if add_item_in_release_form.is_valid():
            add_item_in_release_form.instance.release = release
            item = add_item_in_release_form.cleaned_data['item']
            if item.count < add_item_in_release_form.cleaned_data['count']:
                messages.add_message(request,
                                     messages.ERROR,
                                     'Кол-во выдачи не должно превышать ' + str(item.count))
                return redirect('/releases/details/' + str(id) + '/')
            item.count -= add_item_in_release_form.cleaned_data['count']
            item.save()
            item_in_release, create = ItemInRelease.objects.get_or_create(item=add_item_in_release_form.cleaned_data['item'],
                                                                          release=release,
                                                                          defaults={'count': add_item_in_release_form.cleaned_data['count']})
            if not create:
                item_in_release.count += add_item_in_release_form.cleaned_data['count']
            item_in_release.save()
            messages.add_message(request,
                                 messages.SUCCESS,
                                 'Позиция успешно добавлена')
            return redirect('/releases/details/' + str(id) + '/')
        messages.add_message(request,
                 messages.ERROR,
                 add_item_in_release_form.errors.as_data())
        return redirect('/releases/details/' + str(id) + '/')
    else:
        add_item_in_release_form = ItemInReleaseForm()
    return render(request,
                    'add_item_in_release.html',
                    {'add_item_in_release_form': add_item_in_release_form,
                     'id': id})

и соответствующий html-шаблон для этого шаблона, который загружается в модальное окно Bootstrap из другого шаблона:

<div class="modal fade" id="add-item" tabindex="-1" aria-labelledby="add-item-label" aria-hidden="true"
  dats-bs-focus="false">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title" id="add-item-label">Добавление позиции</h5>
        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
      </div>
      <form action="{% url 'storage:add-item-in-release' id %}" method="POST" class="form" novalidate>
        <div class="modal-body">
          {% csrf_token %}
          <div class="form-floating w-100 mb-3">
            {{ add_item_in_release_form.contract }}
            <label for="count">Договор</label>
          </div>
          <div class="input-group flex-nowrap mb-3">
            <div class="form-floating w-100">
              <input class="form-control" id="item-datalist" placeholder="Позиция" name="item"
                type="text" list="items" autocomplete="off">
              <datalist id="items">
                {% for i in add_item_in_release_form.fields.item.queryset %}
                  <option data-value="{{ i.id }}" data-count="{{ i.count }}" data-contract="{{ i.contract }}" value="{{ i.item.manufacturer.name }} {{ i.item.article }} {{ i.contract }}"></option>
                {% endfor %}
              </datalist>
              <label for="item-datalist">Позиция</label>
            </div>
            <button class="btn btn-outline-secondary" type="button" id="clear-input" title="Очистить">
              <i class="fa fa-eraser"></i>
            </button>
          </div>
          <div class="input-group flex-nowrap mb-3">
            <div class="form-floating w-100">
              {{ add_item_in_release_form.count }}
              <label for="count">Кол-во</label>
            </div>
            <span class="input-group-text w-25" id="count-label" title="Максимальное кол-во"></span>
          </div>
        </div>
        <div class="modal-footer">
          <button type="submit" class="btn btn-outline-success">Добавить</button>
          <button type="button" class="btn btn-outline-primary" data-bs-dismiss="modal">Отменить</button>
        </div>
      </form>
    </div>
  </div>
</div>

Когда я вызываю это представление, страница загружается до 10 секунд, но другие страницы загружаются мгновенно. Почему этот код работает так медленно? Может быть, это потому, что у меня около 1000 опций в datalist? Я не понимаю.

Похоже, что наблюдаемая вами медлительность может быть вызвана несколькими причинами:

  1. Валидация формы: Похоже, что ваше представление выполняет валидацию формы, которая включает операции с базой данных (например, запрос ItemInRelease). Это может замедлить время отклика, особенно если имеется много запросов к базе данных или сложная логика валидации.

  2. Запросы к базе данных: Как вы уже упоминали, наличие около 1000 опций в списке данных также может способствовать медлительности, особенно если каждая опция требует запроса к базе данных для получения соответствующих данных. Рассмотрите возможность оптимизации запросов к базе данных или уменьшения количества опций, если это возможно.

  3. Перенаправления: Представление выполняет перенаправления после обработки отправленных форм или при возникновении ошибок. Перенаправления добавляют дополнительные обходы между клиентом и сервером, что может увеличить время загрузки.

Чтобы повысить производительность представления, вы можете рассмотреть следующие оптимизации:

  • Reduce Database Queries: Старайтесь минимизировать количество запросов к базе данных, особенно в циклах или повторяющихся операциях. Используйте предварительную выборку наборов запросов Django (select_related или prefetch_related) для эффективной выборки связанных объектов.

  • Пакетная обработка: Если возможно, выполняйте пакетную обработку нескольких операций с базой данных вместе, чтобы уменьшить накладные расходы на выполнение отдельных запросов.

  • Кэширование результатов запросов: Рассмотрите возможность кэширования часто используемых данных или результатов запросов с помощью фреймворка кэширования Django. Это поможет снизить нагрузку на вашу базу данных и ускорить последующие запросы.

  • Асинхронная обработка: Если некоторые операции выполняются особенно медленно или требуют много ресурсов, подумайте о том, чтобы переложить их на фоновые задачи с помощью celery Django или аналогичных асинхронных очередей задач.

  • Оптимизация клиентской стороны: Рассмотрите возможность оптимизации кода на стороне клиента, особенно если JavaScript задействован в обработке отправки форм или взаимодействии с даталистом.

Выполнив эти оптимизации, вы сможете повысить производительность вашего представления Django и значительно сократить время загрузки.

Код работает медленно не потому, что в списке 1000 вариантов (что является несколько плохим дизайном фронтенда / UI), а потому, что вы так часто запрашиваете ForeignKeys:

<datalist id="items">
    {% for i in add_item_in_release_form.fields.item.queryset %}
        <option data-value="{{ i.id }}" data-count="{{ i.count }}" data-contract="{{ i.contract }}" value="{{ i.item.manufacturer.name }} {{ i.item.article }} {{ i.contract }}"></option>
    {% endfor %}
</datalist>

По сути, вы должны загрузить в fields.item.queryset метод с именем select_releated и передать manufacturer и article. Не уверен, как это сделать на форме Django, но уверен, что это возможно. Возможно, можно попробовать переопределить магический субметод __init__, но это, вероятно, не самый оптимальный способ (он будет быстрее, но Query будет вызван дважды, потому что для этого есть другой метод).

Вернуться на верх