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

Django按外键过滤查询集并渲染书籍章节树形结构问题

解决Django中外键关联数据树形渲染的问题

一、修正模板中的反向关联调用

你在模板里写错了反向关联的名称——模型chapters_DB对应的反向关联是chapters_db_set(小写模型名+_set),但你写成了chapter_db_set(少了一个s),这就是模板无法渲染章节的核心原因。

模板正确用法示例

假设你的模板需要渲染书籍列表及其下属章节,直接用嵌套循环即可:

<ul>
{% for book in books %}
    <li>{{ book.title }}
        <ul>
        {% for chapter in book.chapters_db_set.all %}
            <li>{{ chapter.chapter_title }}</li>
        {% empty %}
            <li>无章节</li>
        {% endfor %}
        </ul>
    </li>
{% endfor %}
</ul>

二、在views.py中提前组装数据(推荐)

如果觉得模板里写_set不够直观,或者想优化查询性能(避免N+1查询问题),可以在视图层提前把书籍和对应的章节组装好再传给模板:

1. 使用prefetch_related优化查询

# views.py
from django.shortcuts import render
from .models import books_DB

def book_chapter_tree(request):
    # 提前预加载所有书籍的关联章节,避免多次查询数据库
    books = books_DB.objects.prefetch_related('chapters_db_set').all()
    return render(request, 'your_template.html', {'books': books})

模板里依然可以用book.chapters_db_set.all()遍历章节,性能会比直接查询更优。

2. 手动组装结构化数据

如果想让模板语法更简洁,也可以手动把书籍和章节打包成字典:

# views.py
from django.shortcuts import render
from .models import books_DB

def book_chapter_tree(request):
    books_with_chapters = []
    # 用prefetch_related避免重复查询数据库
    for book in books_DB.objects.prefetch_related('chapters_db_set').all():
        books_with_chapters.append({
            'book_info': book,
            'chapters': book.chapters_db_set.all()
        })
    return render(request, 'your_template.html', {'books_data': books_with_chapters})

对应的模板用法:

<ul>
{% for item in books_data %}
    <li>{{ item.book_info.title }}
        <ul>
        {% for chapter in item.chapters %}
            <li>{{ chapter.chapter_title }}</li>
        {% empty %}
            <li>无章节</li>
        {% endfor %}
        </ul>
    </li>
{% endfor %}
</ul>

补充:自定义反向关联名称(可选)

如果不想用Django自动生成的chapters_db_set,可以在定义外键时指定related_name,让名称更直观:

# models.py
class chapters_DB(models.Model):
    # 给外键指定related_name,替换默认的chapters_db_set
    book = models.ForeignKey(books_DB, on_delete=models.CASCADE, db_column='book_id', related_name='chapters')
    chapter_title = models.CharField(max_length=100)

这样不管在shell还是模板里,都可以直接用book.chapters.all()获取章节,语法更简洁。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 09:12:20