Django模板系统
本章将详细介绍Django的模板系统,包括模板语法、模板继承、过滤器、标签、上下文处理器等核心概念,帮助你创建动态和可维护的HTML页面。
模板系统概述
什么是Django模板
Django模板是一个文本文件,可以生成任何基于文本的格式(HTML、XML、CSV等)。模板包含变量和标签,在渲染时会被替换为实际的值。
html
<!-- 基本模板示例 -->
<!DOCTYPE html>
<html>
<head>
<title>{{ page_title }}</title>
</head>
<body>
<h1>欢迎,{{ user.username }}!</h1>
{% if articles %}
<ul>
{% for article in articles %}
<li><a href="{{ article.get_absolute_url }}">{{ article.title }}</a></li>
{% endfor %}
</ul>
{% else %}
<p>暂无文章</p>
{% endif %}
</body>
</html>模板配置
python
# settings.py
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [
BASE_DIR / 'templates', # 项目级模板目录
],
'APP_DIRS': True, # 在应用目录中查找模板
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
'myapp.context_processors.custom_context', # 自定义上下文处理器
],
},
},
]模板目录结构
myproject/
├── templates/ # 项目级模板
│ ├── base.html # 基础模板
│ ├── 404.html # 错误页面
│ ├── 500.html
│ └── registration/ # 认证相关模板
│ ├── login.html
│ └── signup.html
├── blog/
│ └── templates/
│ └── blog/ # 应用级模板
│ ├── article_list.html
│ ├── article_detail.html
│ └── category.html
└── accounts/
└── templates/
└── accounts/
├── profile.html
└── settings.html模板语法基础
变量
html
<!-- 基本变量 -->
{{ variable }}
{{ user.username }}
{{ article.title }}
<!-- 字典访问 -->
{{ data.key }}
{{ user.profile.bio }}
<!-- 列表访问 -->
{{ items.0 }}
{{ articles.first.title }}
<!-- 方法调用(无参数) -->
{{ article.get_absolute_url }}
{{ user.get_full_name }}
<!-- 默认值 -->
{{ variable|default:"默认值" }}
{{ user.email|default:"未设置邮箱" }}标签
html
<!-- 条件判断 -->
{% if user.is_authenticated %}
<p>欢迎,{{ user.username }}!</p>
{% elif user.is_anonymous %}
<p>请先登录</p>
{% else %}
<p>未知用户状态</p>
{% endif %}
<!-- 循环 -->
{% for article in articles %}
<div class="article">
<h3>{{ article.title }}</h3>
<p>{{ article.content|truncatewords:30 }}</p>
<!-- 循环变量 -->
<small>第 {{ forloop.counter }} 篇文章</small>
{% if forloop.first %}
<span class="badge">最新</span>
{% endif %}
{% if forloop.last %}
<hr>
{% endif %}
</div>
{% empty %}
<p>暂无文章</p>
{% endfor %}
<!-- 循环变量详解 -->
{{ forloop.counter }} <!-- 当前循环次数(从1开始) -->
{{ forloop.counter0 }} <!-- 当前循环次数(从0开始) -->
{{ forloop.revcounter }} <!-- 剩余循环次数(到1结束) -->
{{ forloop.revcounter0 }} <!-- 剩余循环次数(到0结束) -->
{{ forloop.first }} <!-- 是否是第一次循环 -->
{{ forloop.last }} <!-- 是否是最后一次循环 -->
{{ forloop.parentloop }} <!-- 父循环对象 -->
<!-- URL生成 -->
<a href="{% url 'blog:article_detail' article.id %}">{{ article.title }}</a>
<a href="{% url 'blog:category' category.slug %}">{{ category.name }}</a>
<!-- 静态文件 -->
{% load static %}
<link rel="stylesheet" href="{% static 'css/style.css' %}">
<img src="{% static 'images/logo.png' %}" alt="Logo">
<!-- 包含其他模板 -->
{% include 'partials/header.html' %}
{% include 'partials/sidebar.html' with articles=latest_articles %}
<!-- 注释 -->
{# 这是单行注释 #}
{% comment %}
这是多行注释
可以包含多行内容
{% endcomment %}过滤器
html
<!-- 字符串过滤器 -->
{{ article.title|upper }} <!-- 转大写 -->
{{ article.title|lower }} <!-- 转小写 -->
{{ article.title|title }} <!-- 标题格式 -->
{{ article.title|capfirst }} <!-- 首字母大写 -->
{{ article.content|truncatewords:30 }} <!-- 截取30个单词 -->
{{ article.content|truncatechars:100 }} <!-- 截取100个字符 -->
{{ article.content|linebreaks }} <!-- 转换换行为<p>和<br> -->
{{ article.content|striptags }} <!-- 移除HTML标签 -->
{{ article.content|escape }} <!-- HTML转义 -->
{{ article.content|safe }} <!-- 标记为安全,不转义 -->
<!-- 数字过滤器 -->
{{ price|floatformat:2 }} <!-- 保留2位小数 -->
{{ number|add:10 }} <!-- 加10 -->
{{ items|length }} <!-- 获取长度 -->
<!-- 日期过滤器 -->
{{ article.created_at|date:"Y-m-d H:i:s" }} <!-- 格式化日期 -->
{{ article.created_at|timesince }} <!-- 距现在多长时间 -->
{{ article.created_at|timeuntil }} <!-- 距未来多长时间 -->
<!-- 列表过滤器 -->
{{ articles|first }} <!-- 第一个元素 -->
{{ articles|last }} <!-- 最后一个元素 -->
{{ articles|slice:":5" }} <!-- 前5个元素 -->
{{ articles|random }} <!-- 随机一个元素 -->
{{ tags|join:", " }} <!-- 用逗号连接 -->
<!-- 链式过滤器 -->
{{ article.title|lower|capfirst }}
{{ article.content|striptags|truncatewords:20 }}
{{ user.email|default:"未设置"|upper }}模板继承
基础模板
html
<!-- templates/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 %}我的网站{% endblock %}</title>
{% load static %}
<link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}">
<link rel="stylesheet" href="{% static 'css/style.css' %}">
{% block extra_css %}{% endblock %}
</head>
<body>
<!-- 导航栏 -->
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container">
<a class="navbar-brand" href="{% url 'home' %}">我的网站</a>
<div class="navbar-nav">
<a class="nav-link" href="{% url 'blog:article_list' %}">文章</a>
<a class="nav-link" href="{% url 'about' %}">关于</a>
{% if user.is_authenticated %}
<a class="nav-link" href="{% url 'profile' %}">{{ user.username }}</a>
<a class="nav-link" href="{% url 'logout' %}">退出</a>
{% else %}
<a class="nav-link" href="{% url 'login' %}">登录</a>
<a class="nav-link" href="{% url 'signup' %}">注册</a>
{% endif %}
</div>
</div>
</nav>
<!-- 消息提示 -->
{% if messages %}
<div class="container mt-3">
{% for message in messages %}
<div class="alert alert-{{ message.tags }} alert-dismissible fade show">
{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
{% endfor %}
</div>
{% endif %}
<!-- 面包屑导航 -->
{% block breadcrumb %}{% endblock %}
<!-- 主要内容 -->
<main class="container my-4">
<div class="row">
<div class="col-md-8">
{% block content %}{% endblock %}
</div>
<div class="col-md-4">
{% block sidebar %}
{% include 'partials/sidebar.html' %}
{% endblock %}
</div>
</div>
</main>
<!-- 页脚 -->
<footer class="bg-dark text-light py-4 mt-5">
<div class="container">
<div class="row">
<div class="col-md-6">
<p>© 2023 我的网站. 保留所有权利.</p>
</div>
<div class="col-md-6 text-end">
{% block footer_extra %}{% endblock %}
</div>
</div>
</div>
</footer>
<!-- JavaScript -->
<script src="{% static 'js/bootstrap.bundle.min.js' %}"></script>
{% block extra_js %}{% endblock %}
</body>
</html>子模板
html
<!-- blog/templates/blog/article_list.html -->
{% extends 'base.html' %}
{% load static %}
{% block title %}文章列表 - {{ block.super }}{% endblock %}
{% block extra_css %}
<link rel="stylesheet" href="{% static 'css/blog.css' %}">
{% endblock %}
{% block breadcrumb %}
<nav aria-label="breadcrumb" class="container mt-3">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="{% url 'home' %}">首页</a></li>
<li class="breadcrumb-item active">文章列表</li>
</ol>
</nav>
{% endblock %}
{% block content %}
<div class="d-flex justify-content-between align-items-center mb-4">
<h1>文章列表</h1>
{% if user.is_authenticated %}
<a href="{% url 'blog:create_article' %}" class="btn btn-primary">写文章</a>
{% endif %}
</div>
<!-- 搜索和过滤 -->
<div class="row mb-4">
<div class="col-md-8">
<form method="get" class="d-flex">
<input type="text" name="search" class="form-control me-2"
placeholder="搜索文章..." value="{{ search_query }}">
<button type="submit" class="btn btn-outline-secondary">搜索</button>
</form>
</div>
<div class="col-md-4">
<select class="form-select" onchange="filterByCategory(this.value)">
<option value="">所有分类</option>
{% for category in categories %}
<option value="{{ category.slug }}"
{% if category.slug == current_category %}selected{% endif %}>
{{ category.name }}
</option>
{% endfor %}
</select>
</div>
</div>
<!-- 文章列表 -->
{% for article in page_obj %}
<article class="card mb-4">
{% if article.featured_image %}
<img src="{{ article.featured_image.url }}" class="card-img-top" alt="{{ article.title }}">
{% endif %}
<div class="card-body">
<h5 class="card-title">
<a href="{{ article.get_absolute_url }}" class="text-decoration-none">
{{ article.title }}
</a>
</h5>
<p class="card-text">{{ article.content|striptags|truncatewords:30 }}</p>
<div class="d-flex justify-content-between align-items-center">
<small class="text-muted">
<i class="fas fa-user"></i> {{ article.author.username }}
<i class="fas fa-calendar ms-2"></i> {{ article.created_at|date:"Y-m-d" }}
<i class="fas fa-eye ms-2"></i> {{ article.views }}
</small>
<span class="badge bg-secondary">{{ article.category.name }}</span>
</div>
</div>
</article>
{% empty %}
<div class="text-center py-5">
<h3>暂无文章</h3>
<p class="text-muted">还没有发布任何文章</p>
{% if user.is_authenticated %}
<a href="{% url 'blog:create_article' %}" class="btn btn-primary">写第一篇文章</a>
{% endif %}
</div>
{% endfor %}
<!-- 分页 -->
{% if page_obj.has_other_pages %}
<nav aria-label="文章分页">
<ul class="pagination justify-content-center">
{% if page_obj.has_previous %}
<li class="page-item">
<a class="page-link" href="?page=1{% if search_query %}&search={{ search_query }}{% endif %}">首页</a>
</li>
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.previous_page_number }}{% if search_query %}&search={{ search_query }}{% endif %}">上一页</a>
</li>
{% endif %}
{% for num in page_obj.paginator.page_range %}
{% if page_obj.number == num %}
<li class="page-item active">
<span class="page-link">{{ num }}</span>
</li>
{% elif num > page_obj.number|add:'-3' and num < page_obj.number|add:'3' %}
<li class="page-item">
<a class="page-link" href="?page={{ num }}{% if search_query %}&search={{ search_query }}{% endif %}">{{ num }}</a>
</li>
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.next_page_number }}{% if search_query %}&search={{ search_query }}{% endif %}">下一页</a>
</li>
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.paginator.num_pages }}{% if search_query %}&search={{ search_query }}{% endif %}">末页</a>
</li>
{% endif %}
</ul>
</nav>
{% endif %}
{% endblock %}
{% block sidebar %}
<!-- 最新文章 -->
<div class="card mb-4">
<div class="card-header">
<h5>最新文章</h5>
</div>
<div class="card-body">
{% for article in latest_articles %}
<div class="mb-2">
<a href="{{ article.get_absolute_url }}" class="text-decoration-none">
{{ article.title|truncatechars:30 }}
</a>
<small class="text-muted d-block">{{ article.created_at|date:"m-d" }}</small>
</div>
{% endfor %}
</div>
</div>
<!-- 热门标签 -->
<div class="card">
<div class="card-header">
<h5>热门标签</h5>
</div>
<div class="card-body">
{% for tag in popular_tags %}
<a href="{% url 'blog:tag' tag.slug %}" class="badge bg-light text-dark me-1 mb-1">
{{ tag.name }}
</a>
{% endfor %}
</div>
</div>
{% endblock %}
{% block extra_js %}
<script>
function filterByCategory(categorySlug) {
const url = new URL(window.location);
if (categorySlug) {
url.searchParams.set('category', categorySlug);
} else {
url.searchParams.delete('category');
}
window.location.href = url.toString();
}
</script>
{% endblock %}自定义模板标签和过滤器
创建模板标签库
python
# blog/templatetags/__init__.py
# 空文件,使目录成为Python包
# blog/templatetags/blog_extras.py
from django import template
from django.utils.safestring import mark_safe
from django.utils.html import format_html
from ..models import Article, Category
import markdown
register = template.Library()
# 简单标签
@register.simple_tag
def total_articles():
"""返回文章总数"""
return Article.objects.filter(published=True).count()
@register.simple_tag
def get_categories():
"""获取所有分类"""
return Category.objects.all()
@register.simple_tag(takes_context=True)
def current_time(context, format_string):
"""获取当前时间"""
from datetime import datetime
return datetime.now().strftime(format_string)
# 包含标签
@register.inclusion_tag('blog/tags/recent_articles.html')
def show_recent_articles(count=5):
"""显示最新文章"""
articles = Article.objects.filter(published=True)[:count]
return {'articles': articles}
@register.inclusion_tag('blog/tags/category_list.html', takes_context=True)
def show_categories(context):
"""显示分类列表"""
categories = Category.objects.all()
return {
'categories': categories,
'request': context['request'],
}
# 赋值标签
@register.simple_tag
def get_popular_articles(count=5):
"""获取热门文章"""
return Article.objects.filter(published=True).order_by('-views')[:count]
# 自定义过滤器
@register.filter
def markdown_to_html(text):
"""将Markdown转换为HTML"""
return mark_safe(markdown.markdown(text))
@register.filter
def multiply(value, arg):
"""乘法过滤器"""
try:
return int(value) * int(arg)
except (ValueError, TypeError):
return 0
@register.filter
def get_item(dictionary, key):
"""从字典获取值"""
return dictionary.get(key)
@register.filter
def add_class(field, css_class):
"""为表单字段添加CSS类"""
return field.as_widget(attrs={'class': css_class})
# 条件标签
@register.simple_tag
def url_replace(request, field, value):
"""替换URL参数"""
dict_ = request.GET.copy()
dict_[field] = value
return dict_.urlencode()
# 复杂的自定义标签
@register.tag
def get_articles_by_category(parser, token):
"""根据分类获取文章"""
try:
tag_name, category_slug, as_var = token.split_contents()
except ValueError:
raise template.TemplateSyntaxError(
f"{token.contents.split()[0]} tag requires exactly two arguments"
)
if not (as_var == 'as'):
raise template.TemplateSyntaxError(
f"{tag_name} tag's second argument should be 'as'"
)
return ArticlesByCategoryNode(category_slug, as_var)
class ArticlesByCategoryNode(template.Node):
def __init__(self, category_slug, var_name):
self.category_slug = template.Variable(category_slug)
self.var_name = var_name
def render(self, context):
try:
category_slug = self.category_slug.resolve(context)
articles = Article.objects.filter(
category__slug=category_slug,
published=True
)
context[self.var_name] = articles
except template.VariableDoesNotExist:
context[self.var_name] = []
return ''使用自定义标签和过滤器
html
<!-- 在模板中使用 -->
{% load blog_extras %}
<!-- 简单标签 -->
<p>网站共有 {% total_articles %} 篇文章</p>
<p>当前时间:{% current_time "%Y-%m-%d %H:%M:%S" %}</p>
<!-- 包含标签 -->
{% show_recent_articles 10 %}
{% show_categories %}
<!-- 自定义过滤器 -->
{{ article.content|markdown_to_html }}
{{ price|multiply:quantity }}
{{ form.email|add_class:"form-control" }}
<!-- 复杂标签 -->
{% get_articles_by_category "technology" as tech_articles %}
{% for article in tech_articles %}
<h3>{{ article.title }}</h3>
{% endfor %}
<!-- URL参数替换 -->
<a href="?{% url_replace request 'page' page_obj.next_page_number %}">下一页</a>包含标签的模板
html
<!-- blog/templates/blog/tags/recent_articles.html -->
<div class="recent-articles">
<h4>最新文章</h4>
<ul class="list-unstyled">
{% for article in articles %}
<li class="mb-2">
<a href="{{ article.get_absolute_url }}" class="text-decoration-none">
{{ article.title|truncatechars:40 }}
</a>
<small class="text-muted d-block">{{ article.created_at|date:"m-d" }}</small>
</li>
{% endfor %}
</ul>
</div>
<!-- blog/templates/blog/tags/category_list.html -->
<div class="category-list">
<h4>文章分类</h4>
<ul class="list-unstyled">
{% for category in categories %}
<li>
<a href="{% url 'blog:category' category.slug %}"
class="{% if request.resolver_match.kwargs.slug == category.slug %}active{% endif %}">
{{ category.name }}
<span class="badge bg-secondary">{{ category.article_set.count }}</span>
</a>
</li>
{% endfor %}
</ul>
</div>上下文处理器
自定义上下文处理器
python
# blog/context_processors.py
from .models import Category, Article
def blog_context(request):
"""博客相关的全局上下文"""
return {
'all_categories': Category.objects.all(),
'recent_articles': Article.objects.filter(published=True)[:5],
'popular_articles': Article.objects.filter(published=True).order_by('-views')[:5],
'site_name': '我的博客',
'site_description': '分享技术和生活',
}
def navigation_context(request):
"""导航相关的上下文"""
navigation_items = [
{'name': '首页', 'url': '/', 'active': request.path == '/'},
{'name': '文章', 'url': '/blog/', 'active': request.path.startswith('/blog/')},
{'name': '关于', 'url': '/about/', 'active': request.path == '/about/'},
]
return {
'navigation_items': navigation_items,
'current_path': request.path,
}
def user_context(request):
"""用户相关的上下文"""
context = {}
if request.user.is_authenticated:
context.update({
'user_article_count': Article.objects.filter(author=request.user).count(),
'user_notifications': get_user_notifications(request.user),
})
return context
def get_user_notifications(user):
"""获取用户通知(示例)"""
# 这里可以实现实际的通知逻辑
return []注册上下文处理器
python
# settings.py
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR / 'templates'],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
'blog.context_processors.blog_context', # 自定义上下文处理器
'blog.context_processors.navigation_context', # 导航上下文
'blog.context_processors.user_context', # 用户上下文
],
},
},
]模板优化和最佳实践
模板缓存
python
# settings.py
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.redis.RedisCache',
'LOCATION': 'redis://127.0.0.1:6379/1',
}
}
# 启用模板缓存
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR / 'templates'],
'APP_DIRS': True,
'OPTIONS': {
'loaders': [
('django.template.loaders.cached.Loader', [
'django.template.loaders.filesystem.Loader',
'django.template.loaders.app_directories.Loader',
]),
],
'context_processors': [
# ... 上下文处理器
],
},
},
]模板片段缓存
html
<!-- 缓存侧边栏 -->
{% load cache %}
{% cache 300 sidebar request.user.id %}
<div class="sidebar">
{% show_recent_articles %}
{% show_categories %}
</div>
{% endcache %}
<!-- 缓存文章列表 -->
{% cache 600 article_list page_obj.number %}
{% for article in page_obj %}
<!-- 文章内容 -->
{% endfor %}
{% endcache %}
<!-- 条件缓存 -->
{% if user.is_authenticated %}
{% cache 300 user_sidebar user.id %}
<!-- 用户专用侧边栏 -->
{% endcache %}
{% else %}
{% cache 3600 anonymous_sidebar %}
<!-- 匿名用户侧边栏 -->
{% endcache %}
{% endif %}模板组织最佳实践
templates/
├── base.html # 基础模板
├── partials/ # 可重用的模板片段
│ ├── header.html
│ ├── footer.html
│ ├── sidebar.html
│ ├── pagination.html
│ └── messages.html
├── layouts/ # 不同的布局模板
│ ├── single_column.html
│ ├── two_column.html
│ └── three_column.html
├── components/ # 组件模板
│ ├── article_card.html
│ ├── comment_form.html
│ └── search_form.html
├── emails/ # 邮件模板
│ ├── welcome.html
│ ├── password_reset.html
│ └── notification.html
└── errors/ # 错误页面模板
├── 404.html
├── 500.html
└── 403.html可重用的模板组件
html
<!-- components/article_card.html -->
<div class="card mb-3">
{% if article.featured_image %}
<img src="{{ article.featured_image.url }}" class="card-img-top" alt="{{ article.title }}">
{% endif %}
<div class="card-body">
<h5 class="card-title">
<a href="{{ article.get_absolute_url }}">{{ article.title }}</a>
</h5>
<p class="card-text">{{ article.content|striptags|truncatewords:20 }}</p>
<div class="card-meta">
<small class="text-muted">
{{ article.author.username }} · {{ article.created_at|date:"Y-m-d" }}
</small>
<span class="badge bg-primary">{{ article.category.name }}</span>
</div>
</div>
</div>
<!-- 使用组件 -->
{% for article in articles %}
{% include 'components/article_card.html' with article=article %}
{% endfor %}响应式模板
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 %}{% endblock %}</title>
{% load static %}
<!-- Bootstrap CSS -->
<link href="{% static 'css/bootstrap.min.css' %}" rel="stylesheet">
<!-- 自定义CSS -->
<link href="{% static 'css/style.css' %}" rel="stylesheet">
{% block extra_css %}{% endblock %}
</head>
<body>
<!-- 移动端导航 -->
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container">
<a class="navbar-brand" href="{% url 'home' %}">我的网站</a>
<!-- 移动端切换按钮 -->
<button class="navbar-toggler" type="button" data-bs-toggle="collapse"
data-bs-target="#navbarNav">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto">
{% for item in navigation_items %}
<li class="nav-item">
<a class="nav-link {% if item.active %}active{% endif %}"
href="{{ item.url }}">{{ item.name }}</a>
</li>
{% endfor %}
</ul>
</div>
</div>
</nav>
<!-- 主要内容 -->
<main class="container-fluid">
<div class="row">
<!-- 内容区域 -->
<div class="col-lg-8 col-md-12">
{% block content %}{% endblock %}
</div>
<!-- 侧边栏 - 在小屏幕上隐藏 -->
<div class="col-lg-4 d-none d-lg-block">
{% block sidebar %}{% endblock %}
</div>
</div>
</main>
<!-- Bootstrap JS -->
<script src="{% static 'js/bootstrap.bundle.min.js' %}"></script>
{% block extra_js %}{% endblock %}
</body>
</html>本章小结
本章详细介绍了Django的模板系统:
关键要点:
- 模板语法:变量、标签、过滤器的使用
- 模板继承:通过extends和block实现代码复用
- 自定义标签:创建自己的模板标签和过滤器
- 上下文处理器:为所有模板提供全局变量
- 模板优化:缓存和性能优化技巧
重要概念:
- DRY原则:通过继承和包含避免重复代码
- 关注点分离:模板只负责表现层逻辑
- 安全性:自动HTML转义和安全标记
- 可维护性:良好的模板组织结构
最佳实践:
- 使用模板继承建立一致的页面结构
- 创建可重用的模板组件
- 合理使用缓存提高性能
- 保持模板逻辑简单
- 使用语义化的HTML结构
在下一章中,我们将学习Django的静态文件管理,了解如何处理CSS、JavaScript和图片等静态资源。