Django 从入门到精通:知行博客实战(6.1) · 第 27 / 36 节

模板局部片段与渐进增强

吴秀峰 3 次阅读 0 条评论 ↓

学习目标

使用 Django 6.0 起提供的 partialdef,把文章卡片提取成可独立渲染的片段,保持整页导航和无 JavaScript 访问可用。

为什么先消除重复再增加交互

相同卡片若在多个页面复制,会出现标题、作者和状态修改不一致。片段让展示结构集中在一处;整页与局部更新共用它,后续加入 Fetch 或 HTMX 时也无需再维护一份 HTML。

片段没有单独的业务权限,调用者仍要查询允许公开的文章。把片段从文件中加载出来,不会自动过滤草稿,也不会自动处理分页 URL。主线保留普通 GET 搜索和分页链接,读者关闭 JavaScript 后仍能完成主要操作。

具体怎么做

  1. 新建 templates/blog/cards.html,用 partialdef 定义 card。
  2. 首页 include blog/cards.html#card,只传入该片段需要的 post。
  3. 用独立脚本 render_to_string 验证只生成卡片,不包含整页文档。
  4. 本章没有引入前端依赖或异步请求。若作为练习加入局部加载,要处理加载中、失败重试、浏览器历史、焦点和屏幕阅读器提示。

本章新增或替换的完整文件

以下路径均相对于练习项目根目录。替换同名文件的全部内容;未列出的文件保持上一章版本。

examples/render_partial.py

from django.template.loader import render_to_string
from blog.models import Post
post = Post.objects.select_related("author", "category").first()
assert post, "先创建文章"
html = render_to_string("blog/cards.html#card", {"post": post})
print(html)
assert "<article>" in html and "<html" not in html

templates/blog/cards.html

{% partialdef card %}
<article><h2><a href="{{ post.get_absolute_url }}">{{ post.title }}</a></h2>
<p class="muted">{{ post.author.username }} · {{ post.category.name }} · {{ post.created_at|date:"Y-m-d" }}</p>
<p>{{ post.body|truncatechars:120 }}</p></article>
{% endpartialdef %}

templates/blog/home.html

{% extends "base.html" %}
{% block content %}
<h1>学习,实践,分享</h1><p class="muted">共 {{ public_count }} 篇公开文章,统计可能延迟 30 秒。</p>
<form method="get" class="panel" role="search">
<label for="q">搜索文章</label><input id="q" name="q" value="{{ q }}" maxlength="100" placeholder="输入标题或正文关键词">
<label for="category">分类</label><select id="category" name="category"><option value="">全部分类</option>
{% for category in categories %}<option value="{{ category.slug }}"{% if selected_category == category.slug %} selected{% endif %}>{{ category.name }}</option>{% endfor %}</select>
<button type="submit">查找</button> <a href="{% url 'blog:home' %}">清除条件</a></form>
{% for post in page_obj %}{% include "blog/cards.html#card" with post=post only %}{% empty %}<p>暂时没有公开文章。</p>{% endfor %}
{% include "blog/pagination.html" %}
{% endblock %}

如何确认成功

python manage.py shell -c "exec(open('examples/render_partial.py').read())"
python manage.py test --settings=config.test_settings

脚本输出一个 article,断言不存在整页 html 标签;首页视觉与上一章一致,搜索分页仍正常。模板中没有多输出一份未使用的卡片。

练习:给卡片补充“阅读文章”文字链接,只修改片段,确认所有首页记录一起更新。然后说明为什么不应在片段内部执行数据库查询或决定文章是否公开。

常见问题

旧 Django 找不到 partialdef? 这是 6.0+ 功能;旧项目可先使用 include 独立模板,本教程锁定 6.1.2。partial 与 include 有什么不同? partial 可以在模板文件中定义命名片段并按 模板#片段 加载,include 常用于加载一个完整模板,两者可配合。加上 partialdef 就变成 SPA 了吗? 没有,它只是服务器端模板复用能力,浏览器交互策略由你设计。

官方参考

把学习连起来

登录后可保存阅读位置、标记章节完成,并在其他设备继续学习。

登录并保存学习进度 →
联系站长

微信:VIPdjango。技术问题建议先在 社区讨论 中交流,方便更多人参与和查阅。

章节讨论 0