Django模板系统

一、课程概述

每天40分玩转Django:Django模板系统-LMLPHP

二、模板基础配置

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);
            }
        });
    });
});

六、最佳实践

  1. 模板组织:

    • 使用清晰的目录结构
    • 根据功能模块划分模板
    • 复用代码放入includes目录
  2. 性能优化:

    • 使用模板片段缓存
    • 合理使用模板继承
    • 避免过多的模板嵌套
  3. 安全考虑:

    • 默认启用HTML转义
    • 谨慎使用safe过滤器
    • 注意XSS攻击防范

七、常见问题和解决方案

  1. 静态文件不显示:
# 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)
  1. 模板找不到:

    • 检查TEMPLATES设置
    • 确认模板文件位置
    • 检查应用是否已安装
  2. 上下文处理器:

# blog/context_processors.py
def categories(request):
    from blog.models import Category
    return {
        'categories': Category.objects.all()
    }

八、作业和练习

  1. 创建一个完整的博客首页模板
  2. 实现文章详情页模板
  3. 添加分类和标签侧边栏
  4. 实现评论系统模板
  5. 添加分页功能

怎么样今天的内容还满意吗?再次感谢朋友们的观看,关注GZH:凡人的AI工具箱,回复666,送您价值199的AI大礼包。最后,祝您早日实现财务自由,还请给个赞,谢谢!

12-14 18:53