当前位置:首页>python>Python 零基础100天—Day62 Django 模板层

Python 零基础100天—Day62 Django 模板层

  • 2026-09-10 02:39:09
Python 零基础100天—Day62 Django 模板层

Django 模板层 — 让页面更丰富

🕐 预计用时:2-3 小时 | 🎯 目标:掌握 Django 模板语法、过滤器、继承、include 和自定义标签


📖 今日目录

  1. Django 模板基础
  2. 变量与标签
  3. 内置过滤器
  4. 模板继承
  5. include 包含
  6. 静态文件
  7. 自定义过滤器
  8. 自定义模板标签
  9. 今日练习
  10. 今日小结

1. Django 模板基础

🎨 render() 的工作方式

# views.py
from django.shortcuts import render

def index(request):
    context = {
        'title': '我的博客',
        'posts': Post.objects.all()[:5],
        'user': request.user,
    }
    return render(request, 'blog/index.html', context)
    #         模板文件路径         传入的变量
<!-- templates/blog/index.html -->
<h1>{{ title }}</h1>
<p>欢迎,{{ user.username }}</p>

{% for post in posts %}
    <h2>{{ post.title }}</h2>
{% endfor %}

🔍 DTL(Django Template Language)vs Jinja2

特性
Jinja2(Flask)
DTL(Django)
变量
{{ var }}{{ var }}
标签
{% tag %}{% tag %}
过滤器
{{ var|upper }}{{ var|upper }}
注释
{# comment #}{# comment #}
URL 生成
url_for('name'){% url 'name' %}
静态文件
url_for('static'){% static 'path' %}
安全转义
默认自动转义
默认自动转义
自定义标签
函数(简单)
注册系统(强大但复杂)

2. 变量与标签

📤 变量输出

<!-- 简单变量 -->
{{ username }}

<!-- 字典访问 -->
{{ user.name }}  或  {{ user.email }}

<!-- 列表访问 -->
{{ items.0 }}  <!-- 第一个元素 -->
{{ items.2 }}  <!-- 第三个元素 -->

<!-- 方法调用(Django 自动调用无参数方法)-->
{{ user.get_full_name }}  <!-- 自动调用方法 -->
{{ post.created_at|date:"Y-m-d" }}  <!-- 但不能传参数 -->

<!-- 转义控制 -->
{{ html_content }}         <!-- 自动转义:<b>显示为文本 -->
{{ html_content|safe }}    <!-- 不转义:渲染为 HTML -->

🔄 控制标签

{# for 循环 #}
<ul>
{% for item in items %}
    <li>{{ forloop.counter }}. {{ item.name }}</li>  {# 从 1 开始计数 #}
    <li>{{ forloop.counter0 }}. {{ item.name }}</li> {# 从 0 开始 #}
    <li>{{ forloop.revcounter }}</li>  {# 倒计数 #}
    <li>{{ forloop.first }}</li>       {# 是否是第一个 #}
    <li>{{ forloop.last }}</li>        {# 是否是最后一个 #}
    <li>{{ forloop.parentloop }}</li>  {# 嵌套时访问外层循环 #}
{% empty %}
    <li>暂无数据</li>  {# 列表为空时显示 #}
{% endfor %}
</ul>

{# if 条件 #}
{% if user.is_authenticated %}
    <p>欢迎,{{ user.username }}</p>
{% elif user.is_staff %}
    <p>管理员面板</p>
{% else %}
    <p>请登录</p>
{% endif %}

{# if 支持的运算 #}
{% if score > 90 %}优秀{% endif %}
{% if name == 'admin' %}管理员{% endif %}
{% if items %}有数据{% endif %}  {# 非空 = True #}
{% if not items %}无数据{% endif %}
{% if a and b %}都为真{% endif %}
{% if a or b %}至少一个为真{% endif %}

{# with 变量缓存(避免重复查询) #}
{% with total=posts.count %}
    <p>共 {{ total }} 篇文章</p>
{% endwith %}

3. 内置过滤器

过滤器
作用
示例
default
空值默认
{{ val|default:"N/A" }}
length
长度
{{ list|length }}
upper/lower
大小写
{{ name|upper }}
title
首字母大写
{{ name|title }}
truncatewords
截断字数
{{ text|truncatewords:30 }}
truncatechars
截断字符
{{ text|truncatechars:100 }}
linebreaks
换行转 HTML
{{ text|linebreaks }}
date
日期格式化
{{ dt|date:"Y-m-d H:i" }}
timesince
距今多久
{{ dt|timesince }}
filesizeformat
文件大小
{{ size|filesizeformat }}
join
列表拼接
{{ list|join:", " }}
add
加法
{{ count|add:5 }}
first/last
首尾元素
{{ list|first }}
json_script
安全 JSON
{{ data|json_script:"mydata" }}
safe
标记安全
{{ html|safe }}
striptags
去除 HTML 标签
{{ html|striptags }}
urlize
URL 变链接
{{ text|urlize }}

📅 日期过滤器详解

{{ post.created_at|date:"Y年m月d日 H:i" }}  <!-- 2026年05月12日 13:15 -->
{{ post.created_at|date:"Y-m-d" }}          <!-- 2026-05-12 -->
{{ post.created_at|timesince }}              <!-- 2 hours, 30 minutes -->
{{ post.created_at|timesince:now }}          <!-- 同上 -->
{{ post.created_at|timeuntil }}              <!-- 距离未来时间 -->

4. 模板继承

<!-- templates/base.html -->
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>{% block title %}我的网站{% endblock %}</title>
    <link rel="stylesheet" href="{% static 'css/style.css' %}">
    {% block extra_css %}{% endblock %}
</head>
<body>
    <nav>
        <a href="{% url 'main:index' %}">首页</a>
        <a href="{% url 'blog:post_list' %}">博客</a>
        {% if user.is_authenticated %}
            <a href="{% url 'blog:post_create' %}">写文章</a>
            <a href="{% url 'auth:logout' %}">退出</a>
        {% else %}
            <a href="{% url 'auth:login' %}">登录</a>
        {% endif %}
    </nav>

    {% block content %}{% endblock %}

    <footer>© 2026 我的网站</footer>

    {% block extra_js %}{% endblock %}
</body>
</html>
{# 子模板继承 #}
{% extends "base.html" %}

{% block title %}文章列表 - 我的博客{% endblock %}

{% block content %}
<h1>最新文章</h1>
{% for post in posts %}
    <article>
        <h2>{{ post.title }}</h2>
        <p>{{ post.summary }}</p>
    </article>
{% endfor %}
{% endblock %}

5. include 包含

{# 创建可复用的组件 #}
{# templates/includes/pagination.html #}
<div class="pagination">
    {% if page_obj.has_previous %}
        <a href="?page={{ page_obj.previous_page_number }}">«</a>
    {% endif %}

    {% for num in page_obj.paginator.page_range %}
        {% if page_obj.number == num %}
            <span class="current">{{ num }}</span>
        {% else %}
            <a href="?page={{ num }}">{{ num }}</a>
        {% endif %}
    {% endfor %}

    {% if page_obj.has_next %}
        <a href="?page={{ page_obj.next_page_number }}">»</a>
    {% endif %}
</div>
{# 在其他模板中使用 #}
{% include "includes/pagination.html" %}

{# 带变量的 include #}
{% include "includes/alert.html" with message="操作成功" type="success" %}

💡 include vs 继承 vs 自定义标签:

• {% extends %}:页面级复用,定义骨架
• {% include %}:组件级复用,插入固定片段
• {% load %} + {% tag %}:逻辑级复用,可传参、可计算


6. 静态文件

<!-- settings.py 配置 -->
STATIC_URL = '/static/'          # URL 前缀
STATIC_ROOT = BASE_DIR / 'staticfiles'  # collectstatic 收集目录
STATICFILES_DIRS = [
    BASE_DIR / 'static',         # 开发时的静态文件目录
]
{# 在模板中引用静态文件 #}
{% load static %}

<link rel="stylesheet" href="{% static 'css/style.css' %}">
<script src="{% static 'js/main.js' %}"></script>
<img src="{% static 'images/logo.png' %}" alt="Logo">

{# 动态静态文件路径 #}
<img src="{% static user.avatar_url %}" alt="头像">

7. 自定义过滤器

# blog/templatetags/blog_tags.py
from django import template
from django.utils.timesince import timesince
from datetime import datetime

register = template.Library()

@register.filter
def time_ago(value):
    """自定义过滤器:距今多久"""
    if isinstance(value, datetime):
        return timesince(value) + ' 前'
    return value

@register.filter
def censor(value, word='***'):
    """敏感词过滤"""
    bad_words = ['敏感词1', '敏感词2']
    for w in bad_words:
        value = value.replace(w, word)
    return value

@register.filter(name='add_class')
def add_class(field, css_class):
    """给表单字段添加 CSS 类"""
    return field.as_widget(attrs={"class": css_class})
{# 使用自定义过滤器 #}
{% load blog_tags %}

<p>{{ post.created_at|time_ago }}</p>  <!-- 2 hours, 30 minutes 前 -->
<p>{{ content|censor }}</p>

{# 表单中 #}
{{ form.username|add_class:"form-control" }}

8. 自定义模板标签

# blog/templatetags/blog_tags.py

@register.simple_tag
def total_posts():
    """返回文章总数"""
    from blog.models import Post
    return Post.objects.count()

@register.simple_tag
def current_time(format_string):
    """返回当前时间"""
    return datetime.now().strftime(format_string)

@register.inclusion_tag('includes/recent_posts.html')
def show_recent_posts(count=5):
    """显示最近文章(inclusion tag)"""
    from blog.models import Post
    posts = Post.objects.order_by('-created_at')[:count]
    return {'posts': posts}
{# 使用自定义标签 #}
{% load blog_tags %}

{# simple_tag #}
<p>共 {% total_posts %} 篇文章</p>
<p>当前时间:{% current_time "%Y-%m-%d %H:%M" %}</p>

{# inclusion_tag(自动渲染模板) #}
{% show_recent_posts 5 %}

💡 三种自定义标签对比:

• @register.filter:过滤器,用 | 调用,只能传 1-2 个参数
• @register.simple_tag:简单标签,用 {% tag %} 调用,可传多个参数
• @register.inclusion_tag:包含标签,自动渲染子模板,适合复用组件


9. 今日练习

🏋️ 练习 1:模板继承

# 创建 base.html + 3 个子模板
# - base.html:导航栏 + content block + 页脚
# - index.html:首页(欢迎信息 + 最新文章)
# - post_list.html:文章列表(分页)
# - about.html:关于页面
# 所有页面共享导航栏和页脚

🏋️ 练习 2:自定义过滤器

# 创建 blog_tags.py:
# 1. @register.filter('truncate_cn') → 中文截断(按字符而非单词)
# 2. @register.filter('highlight') → 高亮关键词(用 <mark> 包裹)
# 3. @register.inclusion_tag → 显示侧边栏(最新文章 + 分类列表)

🏋️ 练习 3:日期显示

# 用 timesince 过滤器实现:
# - 1 分钟内 → "刚刚"
# - 1 小时内 → "X 分钟前"
# - 24 小时内 → "X 小时前"
# - 30 天内 → "X 天前"
# - 超过 30 天 → 显示具体日期

10. 今日小结

知识点
核心内容
模板语法
{{ 变量 }} / {% 标签 %} / {# 注释 #}
控制标签
for/empty/endfor、if/elif/else/endif、with
过滤器
default/length/date/truncatewords/safe/urlize
模板继承
{% extends %} + {% block %},一个 base.html 管理全局
include
{% include %} 插入可复用组件
静态文件
{% load static %} + {% static 'path' %}
自定义过滤器
@register.filter,简单参数传递
自定义标签
simple_tag(多参数)/ inclusion_tag(带子模板)

🚀 明日预告:Day 63 — Django 表单与认证

模板能渲染页面了,但还不能接收用户输入。明天学 Django 表单系统(ModelForm)和内置的用户认证系统——注册、登录、权限控制,Django 全帮你搞定!

轻松时刻:

最新文章

随机文章