Django 模板渲染缓慢

时间:2021-06-26 18:09:10

标签: django jinja2

我正在渲染一个模板,它需要 2-3 秒的时间来完成。页面上有很多元素(10k 个复选框会改变每个查询),我想知道是否有人知道任何有用的技巧来减少加载时间。

网站链接:http://18.207.127.123/search/

仅供参考:此应用程序中没有 Django 表单或表单集! :-) 我正在处理名为 Lemmas 的对象,这些对象将 Forms 作为他们的孩子......如果这引起混淆,抱歉。

更新 #1

无论需要多长时间,它都在这里:

<li class="form-item" data-lemma="{{lemma.id}}" data-group="{{group}}" style="display:none">
    <input type="checkbox" name="{{lemma.name}}@{{lemma.latin}}@{{lemma.homonym_id}}@{{group}}@{{form}}" onchange="countCheckboxes(this)" id="{{lemma.id}}@{{group}}@{{form}}" checked>
    <label for="{{lemma.id}}@{{group}}@{{form}}">{{ form }}</label>
</li>

点方法会减慢速度吗?

更新 #2 - 发现瓶颈

显然,对 10k 项中的每一项进行的所有 {{ var }} 调用都需要花费这么长时间。每个花费约 50-200 毫秒。

更新 #3 - 删除不必要的标签并autoescape关闭

<input id="x"...
<label for="x">

可以替换为

<label>
<input ...>
</label>

保存六个变量调用。

添加

{% autoescape off %}
# body text
{% endautoescape %}

消除了使每个变量安全的需要。

这两个变化使渲染时间减少了大约 50%。

是数据库命中吗?

在检查了我的 django connections 并进行了分析之后,据我所知,我只进行了两次数据库调用。

我的数据结构是一个字典,看起来像:

results_dict = { LemmaObj1 : [form11, form12, form13, ...],
                 LemmaObj2 : [form21, form22, form33, ...],
                 ...

其中 LemmaObjQueryset 对象。生成这个 dict 只需要 0.5 秒。我通过将 formij 放入列表推导式中来强制对惰性查询集进行评估。

views.py

lemma_qs = Lemma.objects.prefetch_related('form_set')

# ...
# some logic goes here
# ...

for lemma in lemma_qs:

    form_qs = lemma.form_set.all()
    form_list = [form.name for form in form_qs if some_condition(form)]

    if form_list:
        results_dict[lemma] = form_list

context['results_dict'] = results_dict

return render(request, "query.html", context)

所有这些都是说我很确定减速不是来自数据库命中。

有问题的模板

在我的 query.html 中,我有两个 for 循环。

query.html

<div class="lemma-box">
    <ol>
        <li class="lemma-title">
            <div class="lemma-col-1">
                lemma [{{results_dict|length}}] (group {{group}})
            </div>
            <div class="lemma-col-2">
                latin
            </div>
            <div class="lemma-col-3">
                homonym id.
            </div>
            <div class="lemma-col-4">
                <input type="button" class="pushable" value="all" onclick="checkAllLemmas('{{group}}', true)"></input>
                <input type="button" class="pushable" value="none" onclick="checkAllLemmas('{{group}}', false)"></input>
            </div>
        </li>
        {% for lemma, form_list in results_dict.items %}
        <li class="lemma-item" data-lemma="{{lemma.id}}" data-group="{{group}}" onclick="activateLemma(this)">
            <div class="lemma-col-1">
                <input type="checkbox" onchange="countCheckboxes(this)" onclick="lemmaToggleAll(this)" id="{{lemma.id}}@{{group}}" checked></input>
                <label for="{{lemma.id}}@{{group}}">
                {{ lemma.name }}
                </label>
            </div>
            <div class="lemma-col-2">
                {{ lemma.latin }}
            </div>
            <div class="lemma-col-3">
                {{ lemma.homonym_id }}
            </div>
            <div class="lemma-col-4">
                {% with form_list|length as total %}
                <span class="counter">(<span class="total">{{ total }}</span>/<span>{{ total }}</span>)</span>
                {% endwith %}
            </div>
        </li>
        {% endfor %}
        {% for item in not_found_items_set %}
        <li class="lemma-item-not-found">
            {{ item }} not found
        </li>
        {% endfor %}
    </ol>
</div>
<div class="form-box">
    <ol>
        <li class="form-title">
            <div class="form-col-1">
                forms (group {{group}})
            </div>
            <div class="form-col-2" data-group="{{group}}">
                <input type="button" class="pushable" value="all" onclick="checkAllForms('{{group}}', true)"></input>
                <input type="button" class="pushable" value="none" onclick="checkAllForms('{{group}}', false)"></input>
            </div>
        </li>   
        {% for lemma, form_list in results_dict.items %}
            {% for form in form_list %}
            <li class="form-item" data-lemma="{{lemma.id}}" data-group="{{group}}" style="display:none">
                <input type="checkbox" name="{{lemma.name}}@{{lemma.latin}}@{{lemma.homonym_id}}@{{group}}@{{form}}" onchange="countCheckboxes(this)" id="{{lemma.id}}@{{group}}@{{form}}" checked>
                <label for="{{lemma.id}}@{{group}}@{{form}}">{{ form }}</label>
            </li>
            {% endfor %}
        {% endfor %}
    </ol>
</div>

这和我要得到的一样快吗?

Jinja2 的答案?

我通过将 jinja2 转换为 query.html 来简单地尝试 query.jinja,它似乎几乎没有任何区别。我是否需要额外的步骤来利用 jinja2?

模型.py

最后,为了检查,我的models.py

models.py

class Form(models.Model):
    name = models.CharField(max_length=100, db_index = True)
    lemma = models.ForeignKey("Lemma", on_delete = models.CASCADE)

    def __str__(self):
        return self.name

class Lemma(models.Model):
    name = models.CharField(max_length=100, db_index = True)
    latin = models.CharField(max_length=100, db_index = True, blank = True, null = True)
    homonym_id = models.IntegerField(null = True)

    def __str__(self):
        return self.name

1 个答案:

答案 0 :(得分:0)

  1. 要做的一件事是通过清楚地考虑您的代码需要做什么来删除尽可能多的模板标签。这将大大减少渲染时间。比如
<input id="x"...
<label for="x">

被替换为

<label>
<input ...>
</label>

并保存一系列模板标签。

  1. 您可以将变量标记为安全,这再次减少了渲染时间,例如
{% autoescape off %}
# body text
{% endautoescape %}
  1. 绝对最好的方法(如果您需要速度并拥有大量数据)是将 JSON 发送到客户端并让 JS 呈现它。这有点混乱,但速度要快得多(可能快 2-3 倍),并且让您可以更灵活地分段操作 DOM。

我现在要寻找解决方案 (3)。