视图与模板 — 渲染文章列表
本章你将学会用 Django 视图从数据库取数据,配合模板渲染出博客首页的文章卡片列表。
视图(View)的作用
视图是 Django 的核心逻辑层,负责:
- 接收用户请求(
request参数) - 从数据库查询需要的数据
- 将数据传给模板渲染 HTML,返回给用户
Django 视图有两种写法:函数视图(FBV) 和类视图(CBV)。
前八章我们优先使用 FBV——它更直观,适合初学者理解请求处理流程。
函数视图基础
一个 Django 视图函数接收 request 参数,返回 HttpResponse 对象。
实例
from django.shortcuts import render
from .models import Post
def index(request):
"""博客首页:展示所有文章"""
# 从数据库查询所有文章(按时间倒序)
posts = Post.objects.all().order_by('-created_at')
# render 的三个参数:
# 1. request — 请求对象
# 2. 模板路径 — blog/index.html
# 3. context 字典 — 传给模板的数据
context = {
'posts': posts,
'title': 'RUNOOB 博客 - 首页'
}
return render(request, 'blog/index.html', context)
render() 是 Django 中最常用的快捷函数,它组合了:加载模板 → 注入数据 → 生成 HTML → 返回响应。
Django 模板语法
Django 模板语法与 Vue3 模板语法非常相似,但有一些关键区别。
| 用途 | Django 模板 | Vue3 模板 |
|---|---|---|
| 输出变量 | {{ variable }} | {{ variable }} |
| 循环 | {% for item in list %}...{% endfor %} | v-for="item in list" |
| 条件 | {% if cond %}...{% endif %} | v-if="cond" |
| URL 生成 | {% url 'name' %} | router-link to="name" |
| 继承布局 | {% extends 'base.html' %} | 组件嵌套 |
关键区别:Django 模板用
{% %}包裹控制标签,Vue3 用v-指令。Django 模板在服务器端渲染——HTML 生成完毕后才发给浏览器;Vue3 在浏览器端渲染——浏览器收到 JS 后才拼接 HTML。
模板继承:base.html
多个页面共享相同的导航栏和页脚,用 模板继承 避免重复。
父模板定义「块(block)」,子模板填充具体内容。
创建 base.html 父模板
实例
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}RUNOOB 博客{% endblock %}</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: #f5f5f5;
color: #333;
}
.navbar {
display: flex; justify-content: space-between; align-items: center;
padding: 16px 40px; background: #fff;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.navbar .logo { font-size: 22px; font-weight: bold; color: #2c3e50; text-decoration: none; }
.navbar a { margin-left: 20px; text-decoration: none; color: #333; }
.container { max-width: 960px; margin: 40px auto; padding: 0 20px; }
.footer { text-align: center; padding: 20px; color: #999; border-top: 1px solid #eee; }
.section-title { font-size: 24px; margin-bottom: 20px; }
</style>
{% block extra_head %}{% endblock %}
</head>
<body>
<!-- 导航栏 -->
<header class="navbar">
<a href="/" class="logo">RUNOOB 博客</a>
<nav>
<a href="/">首页</a>
<a href="#">关于</a>
</nav>
</header>
<!-- 主内容区(子模板填充) -->
<main class="container">
{% block content %}
{% endblock %}
</main>
<!-- 页脚 -->
<footer class="footer">
<p>© 2024 RUNOOB 博客. Powered by Django.</p>
</footer>
</body>
</html>
创建 index.html 子模板
实例
<!-- extends 必须在最开头 -->
{% extends 'blog/base.html' %}
{% block title %}{{ title }}{% endblock %}
{% block content %}
<h2 class="section-title">最新文章</h2>
{% if posts %}
<div class="article-grid">
{% for post in posts %}
<div class="article-card">
<div class="card-content">
<span class="card-category">{{ post.category.name }}</span>
<h3>{{ post.title }}</h3>
<p>{{ post.summary|truncatechars:80 }}</p>
<span class="card-date">{{ post.created_at|date:"Y-m-d" }}</span>
</div>
</div>
{% endfor %}
</div>
{% else %}
<p class="empty-tip">还没有文章,敬请期待。</p>
{% endif %}
{% endblock %}
模板中用到的几个知识点:
| 语法 | 含义 |
|---|---|
{% extends 'blog/base.html' %} | 继承父模板,必须写在第一行 |
{% block content %}...{% endblock %} | 填充父模板中定义的 content 块 |
{{ post.category.name }} | 通过外键访问关联对象的属性 |
{{ post.summary|truncatechars:80 }} | 过滤器:截取前 80 个字符 |
{{ post.created_at|date:"Y-m-d" }} | 过滤器:格式化日期 |
Django 模板中的 过滤器 用管道符
|表示,跟在变量后面,用于格式化输出。常用过滤器:date、truncatechars、length、default。
添加文章列表样式
在 base.html 的 style 标签中追加以下 CSS:
实例
.article-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 24px;
}
.article-card {
background: #fff;
border-radius: 12px;
overflow: hidden;
box-shadow: 0 2px 12px rgba(0,0,0,0.08);
transition: transform 0.2s, box-shadow 0.2s;
}
.article-card:hover {
transform: translateY(-4px);
box-shadow: 0 8px 24px rgba(0,0,0,0.12);
}
.card-content {
padding: 20px;
}
.card-category {
display: inline-block;
padding: 2px 10px;
background: #e8f5e9;
color: #2e7d32;
border-radius: 12px;
font-size: 12px;
margin-bottom: 8px;
}
.article-card h3 {
font-size: 18px;
margin-bottom: 8px;
color: #222;
}
.article-card p {
font-size: 14px;
color: #666;
line-height: 1.6;
margin-bottom: 12px;
}
.card-date {
font-size: 12px;
color: #999;
}
.empty-tip {
text-align: center;
color: #999;
padding: 60px 0;
font-size: 16px;
}
动手:验证首页效果
确认以下几点已做好:
- models.py 中定义了 Post 和 Category 模型,并已 migrate
- Admin 后台已录入至少 3-5 篇文章(归属于不同的分类)
- views.py 中的 index 函数从数据库查询文章并传给模板
- urls.py 中 / 路由指向 index 视图
启动服务器,访问首页,应该能看到由数据库数据动态渲染的文章卡片列表。
本章小结
本章你掌握了 Django 视图和模板的核心配合:views.py 从数据库查询数据并传入 context、render() 渲染模板、base.html 父模板定义布局 + block、子模板 extends 继承并填充内容、Django 模板语法({{ }}、{% %}、过滤器)。
现在博客首页由数据库驱动,不再硬编码。
