【问题标题】:Django initial loading of page taking too longDjango 页面初始加载时间过长
【发布时间】:2021-08-04 17:28:48
【问题描述】:

您好,我是 Django 的初学者,我正在开展一个项目,该项目在每个页面中列出了 100 家公司以及联系人以及所售商品的数量。这是一个例子:

如您所见,页面的初始加载时间非常长。但是当我刷新页面时,它刷新得非常快,因为我正在使用缓存。

这是我的一些其他文件:

models.py



from __future__ import unicode_literals

from django.db import models


class Company(models.Model):
    name = models.CharField(max_length=150)
    bic = models.CharField(max_length=150, blank=True)

    def get_order_count(self):
        orders = self.orders.count()
        return orders

    def get_order_sum(self):
        orders = Order.objects.filter(company=self)
        total_sum = sum([x.total for x in orders])
        return total_sum

    class Meta:
        ordering = ['-id']


class Contact(models.Model):
    company = models.ForeignKey(
        Company, related_name="contacts", on_delete=models.PROTECT)
    first_name = models.CharField(max_length=150)
    last_name = models.CharField(max_length=150, blank=True)
    email = models.EmailField()

    def get_order_count(self):
        orders = self.orders.count()
        return orders


class Order(models.Model):
    order_number = models.CharField(max_length=150)
    company = models.ForeignKey(Company, related_name="orders", on_delete=models.CASCADE)
    contact = models.ForeignKey(Contact, related_name="orders", on_delete=models.SET_NULL, blank=True, null=True)
    total = models.DecimalField(max_digits=18, decimal_places=9)
    order_date = models.DateTimeField(null=True, blank=True)
   
    added_date = models.DateTimeField(auto_now_add=True)
    
    modified_date = models.DateTimeField(auto_now=True)

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

views.py

from django.shortcuts import render
# Create your views here.
from django.views.generic import ListView
from mailer.models import Company, Contact, Order


class IndexView(ListView):
    template_name = "mailer/index.html"
    model = Company
    paginate_by = 100

html

<div class="container">
    <table class="table table-borderless">
    {% if is_paginated %}
    <tr><td>
        {% if page_obj.has_previous %}
        <a href="?page={{ page_obj.previous_page_number }}" class="pager-prev{% if not page_obj.has_previous %} inactive{% endif %}">&laquo;</a>
        {% endif %}
        </td>
        <td></td>
        <td></td>
        <td>
        {% if page_obj.has_next %}
            <a href="?page={{ page_obj.next_page_number }}" class="pager-next{% if not page_obj.has_next %} inactive{% endif %}">&raquo;</a>
        {% endif %}
        </td>
    </tr>
    {% endif %}
    <tr>
        <th>Name</th>
        <th>Order Count</th>
        <th>Order Sum</th>
        <th>Select</th>
    </tr>
        {% for company in company_list %}
        <tr>
            <td>{{ company.name }}</td>
            <td>{{ company.get_order_count }}</td>
            <td>{{ company.get_order_sum|floatformat:2 }}</td>
            <td><input type="checkbox" name="select{{company.pk}}" id=""></td>
        </tr>
            {% for contact in company.contacts.all %}
            <tr>
                <td>&nbsp;</td>
                <td>{{ contact.first_name }} {{ contact.last_name }}</td>
                <td>Orders: {{ contact.get_order_count }}</td>
                <td></td>
            </tr>
            {% endfor %}
        {% endfor %}
    </table>
    </div>

有什么方法可以减少初始加载时间。请告诉我解决此问题的有效方法。

【问题讨论】:

    标签: django django-models django-views


    【解决方案1】:

    每个{{company.get_order_count}} 都会命中数据库。诚然,使用非常简单的查询,但即便如此,它也会减慢速度。

    你想用这个计数来注释对象。使用

    from django.db.models import Count
    
    class IndexView(ListView):
        template_name = "mailer/index.html"
        model = Company
        paginate_by = 100
    
        def get_queryset(self):
            return super().get_queryset().annotate( num_orders=Count('orders') )
    

    并将{{ company.get_order_count }} 替换为{{ company.num_orders }}。这会将 N+1 个 DB 查询变成一个 DB 查询。

    这是最简单的。 get_order_sum 也有类似的问题,几乎可以肯定可以通过另一个涉及django.db.Sum 的注释来解决。抱歉,已经晚了,我的肚子在咕咕叫,我没有信心能直接把那个从我的头顶上拿下来。

    注释的备忘单是here。您可能还需要查看聚合。

    哦,在你的开发环境中安装Django_debug-toolbar。以后每次变慢时,你可以点击那里查看执行了什么 SQL 以及花费了多长时间。

    【讨论】:

    • 各公司联系人的个别订单不再显示,出现问题。
    • 奇怪。不明白那个。 company.contacts.all 应该完全不受影响。不过,这是另外 N 个查询,可以通过将 .prefetch_related('contacts') 添加到查询集来修复。
    • 对不起,我的错。从模板中剪切并粘贴错误的行。注释固定company.get_order_count 不联系。将编辑我的回复。我不知道如何注释预取的联系人...... Django ORM 的经验告诉你,通常你认为不可能的事情,只是令人费解的扭曲。这可能值得另外提出一个问题。
    • 好的,我会在几个小时内尝试这些东西,谢谢。如果我遇到更多问题,我会分享。
    • 感谢加载时间从 50000 毫秒显着减少到 8000 毫秒。这就是我现在所做的return super().get_queryset().prefetch_related('contacts__orders').annotate( num_orders=Count('orders') ).order_by('-id')。如果有任何方法可以减少加载时间,请告诉我。
    猜你喜欢
    • 2011-11-21
    • 2017-09-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多