Django模板系统
一、课程概述
二、模板基础配置
2.1 模板配置
# 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.categories', # 自定义上下文处理器
],
},
},
]
2.2 项目结构
myproject/
├── manage.py
├── myproject/
│ └── settings.py
├── templates/
│ ├── base.html
│ └── blog/
│ ├── post_list.html
│ ├── post_detail.html
│ └── includes/
│ ├── header.html
│ ├── sidebar.html
│ └── footer.html
└── static/
├── css/
├── js/
└── images/
三、模板语法
3.1 基本语法示例
<!-- templates/blog/post_list.html -->
{% extends 'base.html' %}
{% load static %}
{% block title %}博客文章列表{% endblock %}
{% block content %}
<div class="posts">
{# 这是单行注释 #}
{% comment %}
这是多行注释
可以写很多行
{% endcomment %}
{# 变量输出 #}
<h1>{{ page_title }}</h1>
{# 条件判断 #}
{% if posts %}
{# 循环遍历 #}
{% for post in posts %}
<article class="post">
<h2>{{ post.title }}</h2>
{# 过滤器使用 #}
<p>{{ post.body|truncatewords:30|linebreaks }}</p>
<p>作者:{{ post.author.username|default:"匿名" }}</p>
<p>发布时间:{{ post.publish|date:"Y-m-d H:i" }}</p>
</article>
{% empty %}
<p>暂无文章</p>
{% endfor %}
{% else %}
<p>没有找到任何文章</p>
{% endif %}
</div>
{% endblock %}
3.2 常用过滤器
3.3 自定义模板标签和过滤器
# blog/templatetags/blog_tags.py
from django import template
from django.utils.html import mark_safe
import markdown
register = template.Library()
# 自定义过滤器
@register.filter(name='markdown')
def markdown_format(text):
return mark_safe(markdown.markdown(text))
# 自定义简单标签
@register.simple_tag
def total_posts():
from blog.models import Post
return Post.objects.count()
# 自定义包含上下文的标签
@register.inclusion_tag('blog/includes/latest_posts.html')
def show_latest_posts(count=5):
from blog.models import Post
latest_posts = Post.objects.order_by('-publish')[:count]
return {'latest_posts': latest_posts}
四、模板继承
4.1 基础模板
<!-- 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>
{% block css %}
<link rel="stylesheet" href="{% static 'css/base.css' %}">
{% endblock %}
</head>
<body>
<header>
{% include 'includes/header.html' %}
</header>
<div class="container">
<main>
{% block content %}
{% endblock %}
</main>
<aside>
{% block sidebar %}
{% include 'includes/sidebar.html' %}
{% endblock %}
</aside>
</div>
<footer>
{% include 'includes/footer.html' %}
</footer>
{% block js %}
<script src="{% static 'js/base.js' %}"></script>
{% endblock %}
</body>
</html>
4.2 页面模板
<!-- templates/blog/post_detail.html -->
{% extends 'base.html' %}
{% load blog_tags %}
{% block title %}{{ post.title }}{% endblock %}
{% block css %}
{{ block.super }}
<link rel="stylesheet" href="{% static 'css/post.css' %}">
{% endblock %}
{% block content %}
<article class="post-detail">
<h1>{{ post.title }}</h1>
<div class="meta">
<span>作者:{{ post.author.username }}</span>
<span>发布时间:{{ post.publish|date:"Y-m-d H:i" }}</span>
<span>分类:{{ post.category.name }}</span>
</div>
<div class="content">
{{ post.body|markdown }}
</div>
<div class="tags">
{% for tag in post.tags.all %}
<span class="tag">{{ tag.name }}</span>
{% endfor %}
</div>
</article>
{% show_latest_posts 5 %}
{% endblock %}
五、静态文件处理
5.1 静态文件配置
# settings.py
STATIC_URL = '/static/'
STATICFILES_DIRS = [
BASE_DIR / "static",
]
STATIC_ROOT = BASE_DIR / 'staticfiles'
# 开发环境media文件配置
MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR / 'media'
5.2 静态文件使用示例
<!-- templates/blog/includes/sidebar.html -->
{% load static %}
<div class="sidebar">
<div class="profile">
<img src="{% static 'images/avatar.png' %}" alt="头像">
<h3>{{ user.username }}</h3>
</div>
<div class="categories">
<h3>文章分类</h3>
<ul>
{% for category in categories %}
<li>
<a href="{% url 'blog:category' category.slug %}">
{{ category.name }}
<span>({{ category.posts.count }})</span>
</a>
</li>
{% endfor %}
</ul>
</div>
</div>
5.3 CSS示例
/* static/css/base.css */
:root {
--primary-color: #007bff;
--secondary-color: #6c757d;
--background-color: #f8f9fa;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
line-height: 1.6;
color: #333;
background-color: var(--background-color);
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 15px;
display: grid;
grid-template-columns: 3fr 1fr;
gap: 30px;
}
/* 响应式设计 */
@media (max-width: 768px) {
.container {
grid-template-columns: 1fr;
}
}
5.4 JavaScript示例
// static/js/base.js
document.addEventListener('DOMContentLoaded', function() {
// 处理导航菜单
const toggleMenu = document.querySelector('.toggle-menu');
const nav = document.querySelector('nav');
if (toggleMenu) {
toggleMenu.addEventListener('click', function() {
nav.classList.toggle('active');
});
}
// 处理文章点赞
const likeButtons = document.querySelectorAll('.like-button');
likeButtons.forEach(button => {
button.addEventListener('click', async function() {
const postId = this.dataset.postId;
try {
const response = await fetch(`/api/posts/${postId}/like/`, {
method: 'POST',
headers: {
'X-CSRFToken': getCookie('csrftoken')
}
});
const data = await response.json();
this.querySelector('.like-count').textContent = data.likes;
} catch (error) {
console.error('Error:', error);
}
});
});
});
六、最佳实践
-
模板组织:
- 使用清晰的目录结构
- 根据功能模块划分模板
- 复用代码放入includes目录
-
性能优化:
- 使用模板片段缓存
- 合理使用模板继承
- 避免过多的模板嵌套
-
安全考虑:
- 默认启用HTML转义
- 谨慎使用safe过滤器
- 注意XSS攻击防范
七、常见问题和解决方案
- 静态文件不显示:
# urls.py (开发环境)
from django.conf import settings
from django.conf.urls.static import static
if settings.DEBUG:
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
-
模板找不到:
- 检查TEMPLATES设置
- 确认模板文件位置
- 检查应用是否已安装
-
上下文处理器:
# blog/context_processors.py
def categories(request):
from blog.models import Category
return {
'categories': Category.objects.all()
}
八、作业和练习
- 创建一个完整的博客首页模板
- 实现文章详情页模板
- 添加分类和标签侧边栏
- 实现评论系统模板
- 添加分页功能
怎么样今天的内容还满意吗?再次感谢朋友们的观看,关注GZH:凡人的AI工具箱,回复666,送您价值199的AI大礼包。最后,祝您早日实现财务自由,还请给个赞,谢谢!