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

Django中高效获取QuerySet当前对象相邻上下对象的方法

Django 章节上下导航最优实现方案

你判断的完全正确,把对应模块下的所有章节QuerySet强转成Python列表再遍历找相邻项的方案性能非常差——本质是把全量章节数据(包括大段的content正文)全部拉到内存计算,章节量上来之后内存占用和查询耗时都会飙升。最高效的实现是直接在数据库层做条件过滤,仅查询相邻的2条记录,全程不加载多余数据。


第一步:补全可靠的排序依据(必做)

你当前的Chapter模型没有独立的排序字段,靠主键ID或者创建时间排序,后续如果要调整章节顺序、在中间插入新章节,导航逻辑会直接错乱。先给模型加专门的排序字段:

class Chapter(models.Model):
    title         = models.CharField(max_length=100)
    slug          = models.SlugField(blank=True,null=True,)
    module        = models.ForeignKey(Module,on_delete=models.CASCADE,null=True,blank=True,)
    content       = models.TextField()
    # 新增:存储章节在所属模块内的排序序号,同模块下序号不重复
    order         = models.PositiveIntegerField(verbose_name="章节排序", default=0)

    class Meta:
        # 配置全局默认排序规则
        ordering = ['module_id', 'order']

    def __str__(self):
         return self.title

加完字段执行python manage.py makemigrations、python manage.py migrate同步表结构即可,后续维护章节时同步更新order字段值就可以灵活调整顺序。

如果你暂时不想加order字段,想临时用主键ID排序,把后续逻辑里的order替换成id就能跑,但非常不建议长期这么用。


第二步:模型层封装上下章查询逻辑

直接在Chapter模型上加两个方法,用ORM的条件查询+切片取第一条,数据库会自动走索引,单条查询耗时基本在毫秒级:

class Chapter(models.Model):
    # 省略上面已定义的字段和元配置

    def get_prev_chapter(self):
        # 同模块下,排序号比当前章节小的最接近的一条,就是上一章
        return Chapter.objects.filter(
            module=self.module,
            order__lt=self.order
        ).order_by('-order').first()
    
    def get_next_chapter(self):
        # 同模块下,排序号比当前章节大的最接近的一条,就是下一章
        return Chapter.objects.filter(
            module=self.module,
            order__gt=self.order
        ).order_by('order').first()

如果存在没有归属模块(module=None)的游离章节,只需要在两个方法里加个分支判断,单独处理module为空时的全局排序逻辑即可。


第三步:视图与模板调用

函数视图示例

def chapter_detail(request, pk):
    # 用select_related提前把关联的module数据查出来,避免额外查询
    current_chapter = Chapter.objects.select_related('module').get(pk=pk)
    # 直接调用模型方法拿到上下章,仅额外产生2次单条索引查询,无多余数据加载
    prev_chapter = current_chapter.get_prev_chapter()
    next_chapter = current_chapter.get_next_chapter()
    return render(request, 'chapter_detail.html', {
        'current': current_chapter,
        'prev_chapter': prev_chapter,
        'next_chapter': next_chapter
    })

模板导航部分代码

<div class="chapter-nav">
    {% if prev_chapter %}
    <a href="{% url 'chapter_detail' prev_chapter.pk %}" class="nav-prev">
        <span>上一章</span>
        {{ prev_chapter.title }}
    </a>
    {% endif %}

    {% if next_chapter %}
    <a href="{% url 'chapter_detail' next_chapter.pk %}" class="nav-next">
        <span>下一章</span>
        {{ next_chapter.title }}
    </a>
    {% endif %}
</div>

如果你是用slug做章节路由参数,把模板里传pk的位置换成传prev_chapter.slug/next_chapter.slug就行,逻辑不需要改。


性能对比

  • 全量转列表方案:1次全表查询,加载模块下所有章节的全部字段,百章级别耗时通常在几十到上百毫秒,章节越多越慢
  • 本方案:除当前章节查询外,仅额外2次带索引的单条查询,每次只取1条记录,千章级别耗时也稳定在1-2毫秒,性能差距可达百倍以上。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 07:03:27