Динамические страницы в Django: шаблоны и рендеринг
Django предоставляет мощную систему шаблонов на основе текстовых файлов с HTML и специальными тегами. Шаблоны позволяют отделить логику от представления, делая код чище и удобнее для поддержки.
Основные подходы к шаблонизации
Как создать единую структуру страниц с общими элементами?
Наиболее эффективное решение - наследование шаблонов через тег extends и блоки block. Это позволяет определить базовый каркас (header, footer, sidebar) и заполнять только изменяемое содержимое.
<!-- base.html -->
<!DOCTYPE html>
<html>
<head>
<title>{% block title %}Default Title{% endblock %}</title>
</head>
<body>
<header>Шапка сайта</header>
<main>{% block content %}{% endblock %}</main>
<footer>Подвал</footer>
</body>
</html>
Django python html (использование django с html шаблонами)
Дочерний шаблон:
<!-- index.html -->
{% extends "base.html" %}
{% block title %}Главная страница{% endblock %}
{% block content %}
<h1>Добро пожаловать!</h1>
<p>Сегодня {{ current_date|date:"d.m.Y" }}</p>
{% endblock %}
View:
from django.shortcuts import render
from datetime import date
def home(request):
return render(request, 'index.html', {'current_date': date.today()})
Ошибки: если указан неверный путь к шаблону, возникает TemplateDoesNotExist. Решение - проверьте настройку TEMPLATES.DIRS или APP_DIRS. При использовании кэширования шаблонов в production не забывайте сбрасывать кэш после изменений.
Как вывести одну страницу с минимальной структурой?
Если приложение состоит из одной страницы, можно обойтись без наследования, просто передав контекст в шаблон.
<!-- hello.html -->
<h1>{{ greeting }}</h1>
def hello(request):
return render(request, 'hello.html', {'greeting': 'Привет, мир!'})
Как вставлять общие компоненты на разные страницы без наследования?
Тег include позволяет вставить содержимое другого шаблона. Это удобно для меню, сайдбаров или рекламных блоков.
<!-- menu.html -->
<nav>
<a href="/">Главная</a>
<a href="/about">О нас</a>
</nav>
<!-- about.html -->
{% include "menu.html" %}
<h1>О компании</h1>
<p>Информация...</p>
Как выполнить сложные вычисления или запросы внутри шаблона?
Пользовательские теги и фильтры позволяют вынести логику из шаблона в Python. Создайте файл templatetags/custom_tags.py в приложении.
from django import template
register = template.Library()
@register.simple_tag
def multiply(a, b):
return a * b
В шаблоне:
{% load custom_tags %}
<p>Результат: {% multiply 5 7 %}</p>
Как обновлять часть страницы без перезагрузки?
Можно рендерить фрагмент шаблона и возвращать его в ответ на AJAX-запрос. Создайте отдельный шаблон для части страницы.
<!-- comment_list.html -->
<ul>
{% for comment in comments %}
<li>{{ comment.text }}</li>
{% endfor %}
</ul>
def ajax_comments(request):
comments = Comment.objects.all()
return render(request, 'comment_list.html', {'comments': comments})
Типичные проблемы и их решение
- Ошибка TemplateDoesNotExist: проверьте, что файл шаблона находится в правильной директории (в папке templates приложения или в общем каталоге, указанном в DIRS).
- Кэширование шаблонов при разработке: установите в настройках DEBUG = True, чтобы шаблоны перезагружались при каждом запросе. В production используйте кэш, но не забывайте сбросить его после изменений.
- Отсутствие переменной в контексте: если переменная не передана, шаблон молча пропускает тег {{ var }} или показывает пустую строку. Используйте фильтр default или проверку {% if var %}.
- Проблемы со статическими файлами: настройте STATIC_URL и STATICFILES_DIRS. В шаблоне загружайте статику через {% load static %}, а ссылки через {% static 'css/style.css' %}.
Подробные примеры шаблонов
Пример 1: Полное наследование с меню и футером
Базовый шаблон (base.html):
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<title>{% block title %}Мой сайт{% endblock %}</title>
<link rel="stylesheet" href="{% static 'css/style.css' %}">
</head>
<body>
<header>
<nav>{% include "menu.html" %}</nav>
</header>
<main>{% block content %}{% endblock %}</main>
<footer>2025 © Все права защищены</footer>
</body>
</html>
Menu (menu.html):
<a href="{% url 'home' %}">Главная</a>
<a href="{% url 'about' %}">О нас</a>
<a href="{% url 'contact' %}">Контакты</a>
Дочерний шаблон (about.html):
{% extends "base.html" %}
{% block title %}О нас - Мой сайт{% endblock %}
{% block content %}
<h1>О нашей компании</h1>
<p>Мы занимаемся разработкой веб-приложений на Django.</p>
{% endblock %}
View (views.py):
from django.shortcuts import render
def about(request):
return render(request, 'about.html')
Результат (сгенерированный HTML):
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<title>О нас - Мой сайт</title>
<link rel="stylesheet" href="/static/css/style.css">
</head>
<body>
<header>
<nav>
<a href="/">Главная</a>
<a href="/about/">О нас</a>
<a href="/contact/">Контакты</a>
</nav>
</header>
<main>
<h1>О нашей компании</h1>
<p>Мы занимаемся разработкой веб-приложений на Django.</p>
</main>
<footer>2025 © Все права защищены</footer>
</body>
</html>
Пример 2: Использование цикла и фильтра даты
View передает список событий:
def events(request):
events = [
{'title': 'Конференция', 'date': '2025-03-15', 'time': '10:00'},
{'title': 'Семинар', 'date': '2025-04-01', 'time': '14:30'},
]
return render(request, 'events.html', {'events': events})
Шаблон (events.html):
<ul>
{% for event in events %}
<li>
<strong>{{ event.title }}</strong>
<span>{{ event.date|date:"d E Y" }} в {{ event.time }}</span>
</li>
{% empty %}
<li>Мероприятия не найдены</li>
{% endfor %}
</ul>
Результат:
<ul>
<li>
<strong>Конференция</strong>
<span>15 марта 2025 в 10:00</span>
</li>
<li>
<strong>Семинар</strong>
<span>01 апреля 2025 в 14:30</span>
</li>
</ul>
Пример 3: Пользовательский inclusion_tag для вывода последних новостей
Код тега (templatetags/news_tags.py):
from django import template
from ..models import News
register = template.Library()
@register.inclusion_tag('news/latest_news.html')
def latest_news(count=5):
news = News.objects.order_by('-created_at')[:count]
return {'news_list': news}
Шаблон для тега (news/latest_news.html):
<ul>
{% for item in news_list %}
<li><a href="{{ item.get_absolute_url }}">{{ item.title }}</a></li>
{% endfor %}
</ul>
Использование в любом шаблоне:
{% load news_tags %}
<aside>
<h3>Последние новости</h3>
{% latest_news 3 %}
</aside>
Результат (если есть новости):
<aside>
<h3>Последние новости</h3>
<ul>
<li><a href="/news/1/">Обновление версии 5.0</a></li>
<li><a href="/news/2/">Новый курс по Django</a></li>
<li><a href="/news/3/">Конференция разработчиков</a></li>
</ul>
</aside>
Пример 4: Обработка формы и вывод ошибок
View с формой:
from django.shortcuts import render, redirect
from .forms import ContactForm
def contact(request):
if request.method == 'POST':
form = ContactForm(request.POST)
if form.is_valid():
# обработка
return redirect('thanks')
else:
form = ContactForm()
return render(request, 'contact.html', {'form': form})
Шаблон (contact.html):
<form method="post">
{% csrf_token %}
{{ form.as_p }}
{% if form.errors %}
<div class="errors">
{% for field in form %}
{% for error in field.errors %}
<p class="fw-bold">{{ error }}</p>
{% endfor %}
{% endfor %}
</div>
{% endif %}
<button type="submit">Отправить</button>
</form>
Результат при ошибке валидации:
<form method="post">
<input type="hidden" name="csrfmiddlewaretoken" value="...">
<p><label for="id_name">Имя:</label> <input id="id_name" type="text" name="name" maxlength="100" required></p>
<p><label for="id_email">Email:</label> <input id="id_email" type="email" name="email" required></p>
<div class="errors">
<p class="fw-bold">Обязательное поле.</p>
</div>
<button type="submit">Отправить</button>
</form>