You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django页面渲染耗时约2秒的原因及优化方案咨询

为何处理少量数据时页面渲染仍耗时久?如何解决?

现象

页面渲染耗时约2秒,即使返回模拟数据仍缓慢,但返回空字典tasks_by_day时仅需约150ms。已测得tasks函数执行时间约110ms。

视图代码

def tasks(request):
    tasks = Task.objects.filter(owner=request.user.id)

    tasks_by_day = {
        "MONDAY" : [tasks[0]],
        "TUESDAY" : [tasks[1]],
        "WEDNESDAY": [tasks[2]],
        "THURSDAY": [tasks[3]],
        "FRIDAY": [tasks[4]],
        "SATURDAY": [tasks[5]],
        "SUNDAY": [tasks[7]]
    }

    return render(request, "todo/tasks.html", {"tasks_by_day": tasks_by_day})

模型代码

class Task(models.Model):

    # DAY_OF_THE_WEEK_CHOICES, STATUS_CHOICES - constants

    day_of_the_week = models.CharField(
        max_length=10, choices=DAY_OF_THE_WEEK_CHOICES, default=default_day
    )

    status = models.CharField(
        max_length=2, choices=STATUS_CHOICES, default=NOT_COMPLETED
    )

    task_text = models.CharField(max_length=200)

    owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)

问题根源

核心问题出在多次触发数据库查询:

  • Task.objects.filter(...)返回的是Django的QuerySet,它是惰性执行的。当你用tasks[0]、tasks[1]这种索引访问时,每一次索引都会单独执行一次SQL查询来获取对应的数据。你这里一共访问了7次索引,加上初始的filter查询,总共8次数据库请求,大量的IO往返导致了耗时剧增。
  • 另外,硬编码索引的逻辑完全不合理——任务应该按自身的day_of_the_week字段分组,而不是按查询结果的顺序强行分配到星期几,这不仅逻辑错误,还可能在任务数量不足时抛出IndexError。

解决方案

1. 一次性获取所有数据,避免重复查询

先把QuerySet转换成列表,这样只执行一次数据库查询,后续操作都基于内存中的列表:

def tasks(request):
    # 用list()强制执行查询,将所有数据加载到内存
    tasks = list(Task.objects.filter(owner=request.user.id))
    
    # 加入长度判断,避免任务数量不足时抛出索引错误
    tasks_by_day = {
        "MONDAY" : [tasks[0]] if len(tasks)>=1 else [],
        "TUESDAY" : [tasks[1]] if len(tasks)>=2 else [],
        "WEDNESDAY": [tasks[2]] if len(tasks)>=3 else [],
        "THURSDAY": [tasks[3]] if len(tasks)>=4 else [],
        "FRIDAY": [tasks[4]] if len(tasks)>=5 else [],
        "SATURDAY": [tasks[5]] if len(tasks)>=6 else [],
        "SUNDAY": [tasks[7]] if len(tasks)>=8 else []
    }

    return render(request, "todo/tasks.html", {"tasks_by_day": tasks_by_day})

2. 按任务自身的星期字段正确分组(推荐)

抛弃硬编码索引,根据day_of_the_week字段自动分组,这才符合业务逻辑,同时避免多次查询:

from collections import defaultdict

def tasks(request):
    # 一次查询获取所有用户任务
    tasks = Task.objects.filter(owner=request.user.id)
    
    # 初始化分组字典,自动处理空列表
    tasks_by_day = defaultdict(list)
    for task in tasks:
        # 按任务所属的星期添加到对应分组
        tasks_by_day[task.day_of_the_week].append(task)
    
    # 确保所有星期键都存在,防止模板中找不到键报错
    week_days = ["MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY", "SATURDAY", "SUNDAY"]
    for day in week_days:
        if day not in tasks_by_day:
            tasks_by_day[day] = []
    
    return render(request, "todo/tasks.html", {"tasks_by_day": tasks_by_day})

3. 数据库层面分组(大数据量场景优化)

如果用户任务数量较多,可以让数据库直接完成分组,减少Python端的循环处理:

import django.db.models.aggregates

def tasks(request):
    # 定义所有星期的默认结构
    tasks_by_day = {
        "MONDAY": [], "TUESDAY": [], "WEDNESDAY": [],
        "THURSDAY": [], "FRIDAY": [], "SATURDAY": [], "SUNDAY": []
    }
    
    # 按day_of_the_week分组查询,一次性获取每个星期的任务ID集合
    grouped_tasks = Task.objects.filter(owner=request.user.id).values("day_of_the_week").annotate(
        task_ids=django.db.models.aggregates.Collect('id')
    )
    
    # 填充分组数据
    for group in grouped_tasks:
        day = group["day_of_the_week"]
        # 根据ID批量获取任务对象
        tasks = Task.objects.filter(id__in=group["task_ids"])
        tasks_by_day[day] = list(tasks)
    
    return render(request, "todo/tasks.html", {"tasks_by_day": tasks_by_day})

4. 模板辅助优化

如果模板中需要访问Task的关联字段(比如owner的属性),要提前用select_related预取关联数据,避免模板中触发额外查询:

# 预取owner字段,避免模板中访问task.owner时触发新查询
tasks = Task.objects.filter(owner=request.user.id).select_related('owner')

内容的提问来源于stack exchange,提问作者Munewxar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 00:37:46