Django запрос нескольких данных (1k-3k) занимает слишком много времени (минут) - PullRequest
0 голосов
/ 07 февраля 2020

Хорошо, у меня есть приложение django со следующими условиями:

  • Hosted @: PythonAnywhere
  • База данных: SQLite и MySQL

ПРОБЛЕМА: Сначала с несколькими сотнями данных все в порядке, но когда я достигаю нескольких тысяч (3k), это занимает слишком много времени, чтобы загрузить их на простую таблицу.

МОЙ КОД:

models.py

class Outgoing(models.Model):
    base_in = models.ForeignKey('warehouse.Incoming', related_name='out', on_delete = models.SET_NULL, null=True)
    trans_date = models.DateField('Date', default=timezone.now)
    trans_type = models.CharField('Type', max_length=50, choices = OUTGOING_TYPE)
    form_no = models.CharField('FORM No', max_length=20, default=0)
    project_site = models.ForeignKey(ProjectSite, related_name='out_project_site', null=True, on_delete = models.SET_NULL)
    released_by = models.ForeignKey(User, related_name='out_released_by', default='', on_delete = models.SET_NULL, null=True)
    released_to = models.ForeignKey(User, related_name='out_released_to', blank=True, null=True, on_delete = models.SET_NULL)
    released_out = models.ForeignKey(Outsider, related_name='outsider_released_to', blank=True, null=True, on_delete = models.SET_NULL)
    unit = models.ForeignKey(UnitProfile, related_name='user_unit', blank=True, null=True, on_delete = models.SET_NULL)

    quantity = models.DecimalField('Quantity', db_index=True, max_digits=20, decimal_places=2, default=0)
    details = models.CharField('Details', max_length=200, default='')
    attachment = models.FileField('Form', upload_to='incoming_form', blank=True)

    create_date = models.DateTimeField('Date Created', auto_now_add=True)

    def __str__(self):
        return "%s" %(self.trans_date)

    class Meta:
        verbose_name = 'Outgoing'
        verbose_name_plural = 'Outgoings'

views.py

class OutgoingView(ListView):
    model = Outgoing
    template_name = 'warehouse/outgoing_page.html'
    context_object_name = 'all_out'

outgoing_page. html

                                <tbody>
                                    {% for outgoing in all_out%}
                                    <tr>
                                        <td class="text-truncate">{{ outgoing.trans_date }}</td>
                                        <td class="text-truncate">{{ outgoing.trans_type }}</td>
                                        <td class="text-truncate">{{ outgoing.form_no }}</td>
                                        <td class="text-truncate info">{{ outgoing.base_in.item }}</td>
                                        <td class="text-truncate danger">{{ outgoing.quantity|intcomma }}</td>
                                        <td class="text-truncate">{{ outgoing.project_site }}</td>
                                        <td class="text-truncate">{{ outgoing.unit }}</td>
                                        <td class="text-truncate">{{ outgoing.released_by }}</td>
                                        <td class="text-truncate">{{ outgoing.released_to }}</td>
                                        <td class="text-truncate">{{ outgoing.released_out }}</td>
                                        <td class="text-truncate">{{ outgoing.details }}</td>
                                        <td class="text-truncate">
                                            <i class="la la-pencil font-medium-3"></i>
                                        </td>
                                    </tr>
                                    {% endfor %}
                                </tbody>

Что я сделал: - максимально упростил мои взгляды я Можно. Я даже не делал простой сортировки, поскольку я прочитал, что снова попадает в базу данных. - Переход с SQLite на MySQL - Обратился за помощью к парням из pythonany, которые думали, что у меня может не хватать рабочих, они сказали, что мой аккаунт хороший.

Меня удивляет то, что даже когда я пытаюсь загрузить их через Страница администратора, она все еще очень медленная.

Любая помощь будет отличной, спасибо.

Ответы [ 3 ]

2 голосов
/ 07 февраля 2020

Это проблема, связанная с тем, что набор запросов проходит циклически.

Каждый внешний ключ (project_site, Release_by, Release_to, Release_out, Unit) выбирается на каждой итерации l oop.

Суммируя:

# First loads all objects in queryset
{% for outgoing in all_out%}
   ...
   # will issue query
   # select * from ProjectSite where id=outgoing.project_site_id
   <td class="text-truncate">{{ outgoing.project_site }}</td>

   # will issue query
   # select * from UnitProfile where id=outgoing.unit_id
   <td class="text-truncate">{{ outgoing.unit }}</td>

   ... for all related model information being rendered from foreign key relationships

Эта проблема суммируется с проблемой n + 1 запроса , и ее можно избежать с помощью Django select_related функциональность набора запросов.

В вашем представлении вы можете переопределить функцию ListView по умолчанию get_queryset и выбрать соответствующие модели для той, которую вы перечисляете, например, так:

class OutgoingView(ListView):
    model = Outgoing
    template_name = 'warehouse/outgoing_page.html'
    context_object_name = 'all_out'

    def get_queryset(self):
        return Outgoing.objects.select_related('project_site', 'released_by', 'released_to', 'released_out', 'unit')

Что приведет к этим связанные модели, которые будут объединены, когда начальный запрос запущен (на for outgoing in all_out), и значительное увеличение производительности только с одним запросом для заполнения шаблона.

2 голосов
/ 07 февраля 2020

Вы можете рассмотреть возможность добавления нумерации страниц. ListView обеспечивает нумерацию страниц из коробки. Проверьте их пример здесь: https://docs.djangoproject.com/en/3.0/topics/pagination/#paginating -a-listview

Итак, в вашем случае это будет

class OutgoingView(ListView):
    model = Outgoing
    template_name = 'warehouse/outgoing_page.html'
    context_object_name = 'all_out'
    paginate_by = 2

И в представлении

<tbody>
{% for outgoing in all_out %}
<tr>
      <td class="text-truncate">{{ outgoing.trans_date }}</td>
      <td class="text-truncate">{{ outgoing.trans_type }}</td>
      <td class="text-truncate">{{ outgoing.form_no }}</td>
      <td class="text-truncate info">{{ outgoing.base_in.item }}</td>
      <td class="text-truncate danger">{{ outgoing.quantity|intcomma }}</td>
      <td class="text-truncate">{{ outgoing.project_site }}</td>
      <td class="text-truncate">{{ outgoing.unit }}</td>
      <td class="text-truncate">{{ outgoing.released_by }}</td>
      <td class="text-truncate">{{ outgoing.released_to }}</td>
      <td class="text-truncate">{{ outgoing.released_out }}</td>
      <td class="text-truncate">{{ outgoing.details }}</td>
      <td class="text-truncate">
           <i class="la la-pencil font-medium-3"></i>
      </td>
 </tr>
{% endfor %}

<div class="pagination">
<span class="step-links">
    {% if page_obj.has_previous %}
        <a href="?page=1">&laquo; first</a>
        <a href="?page={{ page_obj.previous_page_number }}">previous</a>
    {% endif %}

    <span class="current">
        Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.
    </span>

    {% if page_obj.has_next %}
        <a href="?page={{ page_obj.next_page_number }}">next</a>
        <a href="?page={{ page_obj.paginator.num_pages }}">last &raquo;</a>
    {% endif %}
</span></div>
0 голосов
/ 07 февраля 2020

Большинство полей записей, которые вы использовали в шаблоне (например, это <td class="text-truncate">{{ outgoing.released_by }}</td>), являются полями внешнего ключа, и здесь происходит то, что для каждой Outgoing записи Django выбирает каждую запись, на которую ссылаются через FK, со всеми полями загружен для вызова метода __str__ для них (для связанных записей, а не для полей). В вашем случае это означает +6 запросов для каждого попадания для записи Outgoing. Это проблема n + 1 .

Вы можете рассмотреть возможность использования select related для этих FK с указанием полей, которые вам действительно нужны, без вызова __str__ метода связанных записи.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...